I have been trying to make a simple app where when you tap a circle, it will disappear, and a new one will come up somewhere else.
Here is all my code, edited, but it still does not work.
#import "GameScene.h"
#implementation GameScene
-(void)didMoveToView:(SKView *)view {
[self LabelShow];
}
int Count = 0;
int CountCheck = 0;
-(void) LabelShow {
//Nothing yet
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
while (CountCheck == Count) {
int FNum1 = 350;
int TNum1 = 650;
int newx = (arc4random()%(TNum1-FNum1))+FNum1;
NSLog(#"RandomXCoord: %i", newx);
int FNum2 = 100;
int TNum2 = 700;
int newy = (arc4random()%(TNum2-FNum2))+FNum2;
NSLog(#"RandomYCoord: %i", newy);
//Location to newx and newy
CGPoint location = CGPointMake(newx, newy);
SKSpriteNode *Circle = [SKSpriteNode spriteNodeWithImageNamed:#"Circle.png"];
Circle.xScale = 0.2;
Circle.yScale = 0.2;
Circle.position = location;
//Shows chircle
[self addChild:Circle];
CountCheck++;
for (UITouch *touch in touches)
{
CGPoint locationTouch = [touch locationInNode:self];
if ([Circle containsPoint:locationTouch]) {
NSLog(#"Success!");
[Circle runAction:[SKAction fadeOutWithDuration:0]];
Count++;
}
}
}
}
#end
As I stated, I have the code that puts the circle on the screen in another method. But whenever I run this code (click the circle), the if statement at the bottom does not get executed.
I tried all the things in this thread:
Can't tap SKSpriteNode - no touch detected ios
, but I can't get it to work still, and have changed the code back to the original.
Could anyone tell me how I could be able to get the if statement executed when you tap the SKSpriteNode?
Thank you for reading this question.
Edit I changed the code, but it still doesn't work
Add the following above your Circle.xScale = 0.2; statement
Circle.name = #"Circle";
and replace your for-loop with the following
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"Circle"]) {
NSLog(#"Success!");
[node removeFromParent];
++Count;
}
}
What you need is containsPoint method.
Here is the touchesBeganWithEvent: method you are looking for:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
if ([circle containsPoint:location] {
// executed when you touch a circle
}
}
}
Note that you can add more statements and conditions.
I created a game for myself, and I had to do the same thing for a character in it. I subclassed SKNode, and added the character details (SKSpriteNode, name, SKAction, etc) in the subclassed model. Then I added the UITouchBegin method to the subclass, so my character would listen to touches.
It is a lot easier to use subclassing, as you can leave all the legwork to the subclass, and focus on the rest of your game. Adding this code to your subclass will take care of it all:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self removeFromParent];
}
You can even set your scene as your character class's delegate, and as soon as the character is removed from parentview, you can create a new one.
If you get stuck on the subclass issues, this answer really helped me out. Let me know if any questions and good luck :)
The other responses seem to detect touches on a SKSpriteNode, not a tap. This code detects tap events.
Edit TapMaxDelta to change how much movement is allowed before cancelling the tap gesture.
class TapNode : SKSpriteNode {
// Tap Vars
var firstPoint : CGPoint?
var TapMaxDelta = CGFloat(10)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init() {
let texture = SKTexture(imageNamed: "Test.png")
super.init(texture: texture, color: UIColor.clear, size: texture.size())
isUserInteractionEnabled = true
}
// ================================================================================================
// Touch Functions
// ================================================================================================
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first {
firstPoint = firstTouch.location(in: self)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first, let firstPoint = firstPoint {
let curPoint = firstTouch.location(in: self)
if abs(curPoint.x - firstPoint.x) <= TapMaxDelta && abs(curPoint.y - firstPoint.y) <= TapMaxDelta {
print("tap yo")
}
}
}
}
Related
The default PTArrowCreate class draws arrows pointing to the user's initial tap on the screen. I want arrows to be pointing at the place where user did finish dragging finger.
Please give me a clue how can i achieve this.
There isn't currently a built-in option for this, but you can implement this via subclassing. Arrow annotations are created using the tool PTAnnotCreate, which you can subclass by registering a subclass before the PTDocumentViewController is created:
[PTOverrides overrideClass:[PTArrowCreate class] withClass:[FWArrowCreate class]];
Then swap the head with the tail of the arrow in the subclass as follows:
#interface FWArrowCreate : PTArrowCreate
#end
#implementation FWArrowCreate
-(void)swapStartAndEndPoints
{
CGPoint savedStartPoint = self.startPoint;
self.startPoint = self.endPoint;
self.endPoint = savedStartPoint;
}
-(void)drawRect:(CGRect)rect
{
[self swapStartAndEndPoints];
[super drawRect:rect];
[self swapStartAndEndPoints];
}
- (BOOL)pdfViewCtrl:(PTPDFViewCtrl*)pdfViewCtrl onTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self swapStartAndEndPoints];
BOOL result = [super pdfViewCtrl:pdfViewCtrl onTouchesEnded:touches withEvent:event];
[self swapStartAndEndPoints];
return result;
}
#end
The same answer in Swift:
class MyArrowCreate: PTArrowCreate {
override func draw(_ rect: CGRect) {
swapPoints()
super.draw(rect)
swapPoints()
}
override func pdfViewCtrl(_ pdfViewCtrl: PTPDFViewCtrl, onTouchesEnded touches: Set<UITouch>, with event: UIEvent?) -> Bool {
swapPoints()
let result = super.pdfViewCtrl(pdfViewCtrl, onTouchesEnded: touches, with: event)
swapPoints()
return result
}
private func swapPoints() {
let tmpPoint = startPoint
startPoint = endPoint
endPoint = tmpPoint
}
}
This website describes what I want to achieve under the 'Handling a Complex Multitouch Sequence' section. Unfortunately, it is shown in Objective-C. I can translate most of it into Swift, but when it comes to a few lines, like the lines with CFDictionary, I honestly have no idea what's going on.
If somebody could help me understand/translate listings 3-4 and 3-5 or suggest another method for handling multi-touch events, I would be very grateful. I have left '???' where I am not sure.
Here is the Obj-C (storing touch initial location):
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self cacheBeginPointForTouches:touches];
}
- (void)cacheBeginPointForTouches:(NSSet *)touches {
if ([touches count] > 0) {
for (UITouch *touch in touches) {
CGPoint *point = (CGPoint *)CFDictionaryGetValue(touchBeginPoints, touch);
if (point == NULL) {
point = (CGPoint *)malloc(sizeof(CGPoint));
CFDictionarySetValue(touchBeginPoints, touch, point);
}
*point = [touch locationInView:view.superview];
}
}
}
Here is retrieving the initial location:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CGAffineTransform newTransform = [self incrementalTransformWithTouches:touches];
}
- (CGAffineTransform)incrementalTransformWithTouches:(NSSet *)touches {
NSArray *sortedTouches = [[touches allObjects] sortedArrayUsingSelector:#selector(compareAddress:)];
// Other code here
CGAffineTransform transform = CGAffineTransformIdentity;
UITouch *touch1 = [sortedTouches objectAtIndex:0];
UITouch *touch2 = [sortedTouches objectAtIndex:1];
CGPoint beginPoint1 = *(CGPoint *)CFDictionaryGetValue(touchBeginPoints, touch1);
CGPoint currentPoint1 = [touch1 locationInView:view.superview];
CGPoint beginPoint2 = *(CGPoint *)CFDictionaryGetValue(touchBeginPoints, touch2);
CGPoint currentPoint2 = [touch2 locationInView:view.superview];
// Compute the affine transform
return transform;
}
Here's what I've managed:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
cacheBeginPoint(touches: touches)
}
func cacheBeginPoint(touches: Set<UITouch>) {
if touches.count > 0 {
for touch: UITouch in touches {
var point: CGPoint? = CFDictionaryGetValue(???)
if point == nil {
point = ???
CFDictionarySetValue(???)
}
point = touch.location(in: view?.superview)
}
}
}
and:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
var newTransform: CGAffineTransform = incrementalTransform(touches: touches)
}
func incrementalTransform(touches: Set<UITouch>) -> CGAffineTransform {
var sortedTouches: NSArray = ???
var transform = CGAffineTransform.identity
var touch1: UITouch? = sortedTouches[0]
var touch2: UITouch? = sortedTouches[1]
var beginPoint1: = CFDictionaryGetValue(???)
var currentPoint1: = touch1?.location(in: view?.superview)
var beginPoint2: = CFDictionaryGetValue(???)
var currentPoint2: = touch2?.location(in: view?.superview)
return transform
}
I have a simple piece of code in which i have overridden touchesBegan,Ended,Cancelled(empty block) and touchesMoved.
If i click ( i'm testing on Desktop PC ) with the mouse touchesBegan it's called, but touchesEnded is called only when i move finger for a while. That makes impossible to recognize a single tap or a drag of the finger, and handle them differently.
I don't understand if this is an emulator problem or i am misunderstanding the whole process. Did you have the same problem?
I have a simple solution for my application, like check a "first move" variable in touchesBegan, but this is a pure technical question.
Thank you in advance.
This is all i use, apart from drawRect that it's not important.
I guess it's not a problem in my code.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.allObjects[0] as UITouch
let touchPoint = touch.locationInView(self)
println("touchesBegan")
if (Draw!){
path.moveToPoint(touchPoint)
path.addLineToPoint(touchPoint)
self.setNeedsDisplay()
}
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.allObjects[0] as UITouch
let touchPoint = touch.locationInView(self)
println("touchesMoved")
if (Draw!){
path.addLineToPoint(touchPoint)
self.setNeedsDisplay()
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.allObjects[0] as UITouch
let touchPoint = touch.locationInView(self)
println("touchesEnded")
if (Draw!){
path.addLineToPoint(touchPoint
path = UIBezierPath() // Create new path for next line
self.setNeedsDisplay()
}
}
I am quite sure this is an emulator problem.
Please see the code below. It works like a charm at my end.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
didMove = false;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
didMove = true;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (didMove == true)
{
//finger was moved
NSLog(#"finger was moved");
}
else
{
//it's a tap
NSLog(#"finger not moved it's a tap");
}
}
While we are at it, can I bring your attention to UIGestureRecognizers? Try to use them since they make life a breeze.
I am having trouble creating a function "touchesBegan" and then creating a UIPoint and UITouch constant or variable that holds an x and y coordinate. I have the exact code I want in Objective-C but I do not know what it's equivalent is in Swift. Here is the Objective-C code which I want to basically translate into Swift code...
NOTE: This is a Single View Application, NOT a game... Thanks in advance.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self.view];
if (point.x < 160) {
var = 10;
}
else{
var = 20;
}
}
As of Swift 1.2, use this
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)
{
var touch = touches.first as! UITouch
var point = touch.locationInView(self)
if point.x < 160 {
var = 10;
}
else{
var = 20;
}
}
Where's the problem?
var touch = touches.anyObject() as! UITouch
var point = touch.locationInView(self.view)
if point.x < 160 {
var variableName = 10;
}
else{
var variableName = 20;
}
Swift 1.2 changed the syntax for touchesBegan. See the UIResponder Reference.
func touchesBegan(_ touches: Set<NSObject>, withEvent event: UIEvent)
And don't forget to reference the super.
super.touchesBegan(touches, withEvent: event)
Here is an edited example of implementing this code from techotopia.com which might give you more information on the other three touches functions.
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touch = touches.anyObject() as! UITouch
var point = touch.locationInView(self.view)
// Insert if statements
super.touchesBegan(touches, withEvent: event)
}
I'm building a sprite kit based game, and the lack of "right click" is really making it hard to convey some important information to my users. As a solution, I'm thinking about gestures like long press, two finger tap, etc.
How can one implement gestures on a SKSpriteNode?
Here's what I'm currently using to get a button-like behavior when an SKSpriteNode is touched.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self selectSkill:YES];
}
Before UIGestureRecognizer, you kept state variables that tracked where touches where and when they started. Here's a swift solution where buttonTouched: is a method that checks whether the UITouch was on the button you're checking.
var touchStarted: NSTimeInterval?
let longTapTime: NSTimeInterval = 0.5
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
if let touch = touches.anyObject() as? UITouch {
if buttonTouched(touch) {
touchStarted = touch.timestamp
}
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
if let touch = touches.anyObject() as? UITouch {
if buttonTouched(touch) && touchStarted != nil {
let timeEnded = touch.timestamp
if timeEnded - touchStarted! >= longTapTime {
handleLongTap()
} else {
handleShortTap()
}
}
}
touchStarted = nil
}
override func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!) {
touchStarted = nil
}
Here is a good component that can help https://github.com/buddingmonkey/SpriteKit-Components
There is no easy way to do it, but one way I can think of would be to add a sub SKView to your SKScene and place an UIImageView as the only thing inside that SKView. Then you can add a gesture recognizer like normal to the SKView.
Here's an example of what I'm talking about:
UIImageView *button = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"ButtonSprite"]];
SKView *spriteNodeButtonView = [[SKView alloc] initWithFrame:CGRectMake(100, 100, button.frame.size.width, button.frame.size.height)];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(someMethod:)];
[spriteNodeButtonView addGestureRecognizer:tap];
[spriteNodeButtonView addSubview:button];
You can put the SKView wherever you want and use any of the gesture recognizers on an SKView: UITapGestureRecognizer, UILongPressGestureRecognizer, UISwipeGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer, UIPanGestureRecognizer, or UIScreenEdgePanGestureRecognizer.
Then for your method implementation do something like this:
-(void)someMethod:(UITapGestureRecognizer *)recognizer {
CGPoint touchLoc = [recognizer locationInView:self.view];
NSLog(#"You tapped the button at - x: %f y: %f", touchLoc.x, touchLoc.y);
}