I have created a scene where I would like to have the character image move throughout the circle that I have created.
the character is an image view and the box containing the circle is an image view.
as of right now I have the image moving but it is throughout the entire scene. How would I limit the movement to just within the circle?
here is the code that I have so far:
.h
#interface FinalProjectViewController : UIViewController {
IBOutlet UIImageView *romo;
CGPoint pos;
NSTimer *romoMove;
}
-(void)romoMoving;
.m
- (void)viewDidLoad {
romoMove = [NSTimer scheduledTimerWithTimeInterval:0.09 target:self
selector:#selector(romoMoving) userInfo:nil repeats:YES];
pos = CGPointMake(5.0, 4.0);
}
-(void)romoMoving
{
romo.center = CGPointMake(romo.center.x+pos.x, romo.center.y+pos.y);
if (romo.center.x>170||romo.center.x<0) {
pos.x = -pos.x;
}
if (romo.center.y>180||romo.center.y<0) {
pos.y = -pos.y;
}
}
I was thinking the best way to go about it was to find the points of the circle that I would like the character to hit then set those as the points as it should move to. I am not sure if that would be the best way to do this, nor was I very sure on how to accomplish this.
Related
Im making a scene for my game. Right now im working for IPhone 5-5s-5c screens. The scene is bigger than the normal IPhone 5-5s-5c`s screen widths so the simulated size is freeform.
Now, your supposed to move this image between obstacles. and the view/ camera is supposed to follow the uiimageview.
Here is some code on how i move the image
-(IBAction)left { goLeft = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(goLeft) userInfo:nil repeats:YES]; if (goLeft == nil) { goLeft = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(goLeft) userInfo:nil repeats:YES]; }
}
-(IBAction)stopLeft { [goLeft invalidate]; goLeft = nil;
}
-(void)goLeft { ball2.center = CGPointMake(ball2.center.x -3.5, ball2.center.y);{
// left is connected to touch down
// stopLeft is connected to Touch up inside
// Thanks for any help :)
#cilus-stian The previous version of your question was tagged as SpriteKit, so I'll suppose you use SpriteKit. If you don't use it, please let me know, and I'll delete my answer which becomes irrelevant.
So what you want is your camera to follow your image (ball2).
You can simply create a SKCameraNode which does exactly what you want (sorry I don't know the Objc syntax) :
// Declare your camera
var camera: SKCameraNode = SKCameraNode()
// Add it to your scene (didMoveToView or init)
self.camera = camera
// In the update() function
self.camera.position = ball2.position
More details about SKCameraNode
Also, not sure how/why you use UIImageView, but maybe you should consider using SKSpriteNode to display your ball
I'm following Ray Wenderlich's 'iOS Games by Tutorials' & I got everything in my world setup & working: The entire game is in Landscape mode & there's one SKNode called _worldNode & everything, except _uiNode (in-game UI), is added to it. Player character walks to a touched location & _worldNode moves under him like a treadmill. However, like all functionality (or as they call it: "juice") addicts I wanted to add zoom in/out functionality through UIPinchGestureRecognizer by scaling _worldNode, which I did. But now every time I zoom in, the "camera" moves to the bottom left. Zooming out moves the view to the top right of the screen. It's a mess. I need the view to stay centered on the player character & I've tried everything I could come up with & find online. The closest thing I came to was using the technique from SKNode scale from the touched point but I still get the bloody bottom left/top right mess. I realized this mess happens only when I update the camera/view (it's really _worldNode.position). Therefore, 'didSimulatePhysics' or 'didFinishUpdate' methods don't help. In fact, even a one time button that slightly moves/updates the camera view (_worldNode.position) still gives me the bottom left/top right problem. Here is my code. I hope someone can take a look & tell me what to modify to get things working.
#interface GameScene () <SKPhysicsContactDelegate, UIGestureRecognizerDelegate>
{
UIPinchGestureRecognizer *pinchGestureRecognizer;
}
//Properties of my GameScene.
#property SKNode *worldNode;
#property etc. etc.
//Called by -(id)initWithSize:(CGSize)size method & creates the in-game world.
-(void)createWorld
{
[_worldNode addChild:_backgroundLayer];
[self addChild:_worldNode];
self.anchorPoint = CGPointMake(0.5, 0.5); //RW tutorial did it this way.
_worldNode.position = CGPointMake(-_backgroundLayer.layerSize.width/2, -_backgroundLayer.layerSize.height/2); //Center.
//Then I add every node to _worldNode from this point on.
}
//Neccessary for gesture recognizers.
-(void)didMoveToView:(SKView *)view
{
pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleZoomFrom:)];
[view addGestureRecognizer:pinchGestureRecognizer];
}
//"Camera" follows player character. 'didSimulatePhysics' doesn't help either.
-(void)didFinishUpdate
{
//IF '_pinchingScreen' == YES then screen pinching is in progress. Thus, camera position update will seize for the duration of pinching.
if (!_pinchingScreen)
{
_worldNode.position = [self pointToCenterViewOn:_player.position];
}
}
//Method that is called by my UIPinchGestureRecognizer. Answer from: https://stackoverflow.com/questions/21900614/sknode-scale-from-the-touched-point?lq=1
-(void)handleZoomFrom:(UIPinchGestureRecognizer*)recognizer
{
CGPoint anchorPoint = [recognizer locationInView:recognizer.view];
anchorPoint = [self convertPointFromView:anchorPoint];
if (recognizer.state == UIGestureRecognizerStateBegan)
{
// No code needed for zooming...
_player.movementMode = 2; //Stop character from moving from touches.
_pinchingScreen = YES; //Notifies 'didFinishUpdate' method that pinching began & camera position update should stop for now.
}
else if (recognizer.state == UIGestureRecognizerStateChanged)
{
//Technique from the above Stack Overflow link - Commented out.
// CGPoint anchorPointInMySkNode = [_worldNode convertPoint:anchorPoint fromNode:self];
//
// [_worldNode setScale:(_worldNode.xScale * recognizer.scale)];
//
// CGPoint mySkNodeAnchorPointInScene = [self convertPoint:anchorPointInMySkNode fromNode:_worldNode];
// CGPoint translationOfAnchorInScene = CGPointSubtract(anchorPoint, mySkNodeAnchorPointInScene);
//
// _worldNode.position = CGPointAdd(_worldNode.position, translationOfAnchorInScene);
//
// recognizer.scale = 1.0;
//Modified scale: 2.0
if(recognizer.scale > _previousWorldScale)
{
_previousWorldScale = recognizer.scale;
CGPoint anchorPointInMySkNode = [_worldNode convertPoint:anchorPoint fromNode:self];
[_worldNode setScale:2.0];
CGPoint worldNodeAnchorPointInScene = [self convertPoint:anchorPointInMySkNode fromNode:_worldNode];
CGPoint translationOfAnchorInScene = CGPointSubtract(anchorPoint, worldNodeAnchorPointInScene);
_worldNode.position = CGPointAdd(_worldNode.position, translationOfAnchorInScene);
//[_worldNode runAction:[SKAction scaleTo:2.0 duration:0]]; //This works too.
}
//Original scale: 1.0
if(recognizer.scale < _previousWorldScale)
{
_previousWorldScale = recognizer.scale;
CGPoint anchorPointInMySkNode = [_worldNode convertPoint:anchorPoint fromNode:self];
[_worldNode setScale:1.0];
CGPoint worldNodeAnchorPointInScene = [self convertPoint:anchorPointInMySkNode fromNode:_worldNode];
CGPoint translationOfAnchorInScene = CGPointSubtract(anchorPoint, worldNodeAnchorPointInScene);
_worldNode.position = CGPointAdd(_worldNode.position, translationOfAnchorInScene);
//[_worldNode runAction:[SKAction scaleTo:1.0 duration:0]]; //This works too.
}
}
else if (recognizer.state == UIGestureRecognizerStateEnded)
{
// No code needed here for zooming...
_pinchingScreen = NO; //Notifies 'didFinishUpdate' method that pinching has stopped & camera position update should resume.
_player.movementMode = 0; //Resume character movement.
}
}
So could anyone please tell me, by looking at the above code, why the camera/view shifts to the bottom left upon zooming in? I've sat several days on this problem & I still can't figure it out.
Thanks to JKallio, who wrote detailed code in his answer to Zoom and Scroll SKNode in SpriteKit, I've been able to find a piece of code that solves the problem. There's a method called 'centerOnNode' that is small, elegant & solves my problem perfectly. Here it is for anyone that just needs that:
-(void) centerOnNode:(SKNode*)node
{
CGPoint posInScene = [node.scene convertPoint:node.position fromNode:node.parent];
node.parent.position = CGPointMake(node.parent.position.x - posInScene.x, node.parent.position.y - posInScene.y);
}
Then you call that method inside your 'didSimulatePhysics' or inside 'didFinishUpdate' like so:
//"Camera" follows player character.
-(void)didFinishUpdate
{
//IF '_pinchingScreen' == YES then screen pinching is in progress. Thus, camera position update will seize for the duration of pinching.
if (!_pinchingScreen)
{
if (_previousWorldScale > 1.0) //If _worldNode scale is greater than 1.0
{
[self centerOnNode:_player]; //THIS IS THE METHOD THAT SOLVES THE PROBLEM!
}
else if (_previousWorldScale == 1.0) //Standard _worldNode scale: 1.0
{
_worldNode.position = [self pointToCenterViewOn:_player.position];
}
}
}
P.S. Just remember the question wasn't HOW to zoom in. But how to fix the issue with the camera once the world is ALREADY zoomed in.
I am using this code to implement infinite looping, but I'v got gaps for 1-2 seconds every time the offscreen image coordinates are changed. Why do they appear? How to fix it? I am also using SpriteBuilder.
#import "MainScene.h"
static const CGFloat scrollSpeed =100.f;
#implementation MainScene{
CCPhysicsNode *_world;
CCNode *_oneb;
CCNode *_twob;
NSArray *_bb;
}
- (void)didLoadFromCCB {
_bb = #[_oneb, _twob];
}
-(void)update:(CCTime)delta{
_world.position=ccp(_world.position.x - (scrollSpeed * delta), _world.position.y ); // moving world
for (CCNode *ground in _bb) {
// get the world position of the ground
CGPoint groundWorldPosition = [_world convertToWorldSpace:ground.position];
// get the screen position of the ground
CGPoint groundScreenPosition = [self convertToNodeSpace:groundWorldPosition];
// if the left corner is one complete width off the screen, move it to the right
if (groundScreenPosition.x <= (-1 * ground.contentSize.width)) {
ground.position = ccp(ground.position.x + 2 * ground.contentSize.width, ground.position.y);
}
}
}
#end
EDIT: I changed -1 to -0.5. Works fine!
Seems like you are using small image for iPhone 3.5-inch on iPhone 4-inch simulator. What resolution of your background image?
EDIT: In my game I have an infinite loop, too. Maybe my code may help you? First background sprite should be 1137x640, second 1136x640. And you will never have gaps again! Hope it helps.
init method:
backgroundSprite = [CCSprite spriteWithFile:#"background.png"];
backgroundSprite.anchorPoint = ccp(0,0);
backgroundSprite.position = ccp(0,0);
[self addChild:backgroundSprite z:0];
backgroundSprite2 = [CCSprite spriteWithFile:#"background2.png"];
backgroundSprite2.anchorPoint = ccp(0,0);
backgroundSprite2.position = ccp([backgroundSprite boundingBox].size.width,0);
[self addChild:backgroundSprite2 z:0];
tick method:
backgroundSprite.position = ccp(backgroundSprite.position.x-1,backgroundSprite.position.y);
backgroundSprite2.position = ccp(backgroundSprite2.position.x-1,backgroundSprite2.position.y);
if (backgroundSprite.position.x<-[backgroundSprite boundingBox].size.width) {
backgroundSprite.position = ccp(backgroundSprite2.position.x+[backgroundSprite2 boundingBox].size.width,backgroundSprite.position.y);
}
if (backgroundSprite2.position.x<-[backgroundSprite2 boundingBox].size.width) {
backgroundSprite2.position = ccp(backgroundSprite.position.x+[backgroundSprite boundingBox].size.width,backgroundSprite2.position.y);
}
So I'm working on a quick animation of a car and I want the tires to rotate and translate forward... but for some reason they were sliding forward but then I added the code to rotate them and now they rotate and jump random x positions back and forth, it's super confusing and I have NO clue what's happening.. did I leave out a required framework or something?
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
frame = [NSTimer scheduledTimerWithTimeInterval:1/40.f
target:self
selector:#selector(enterFrame)
userInfo:nil
repeats:YES];
}
-(void)enterFrame {
//spin += +0.5;
spin = 0.5;
//wheel.center = CGPointMake(wheel.center.x+0.5, wheel.center.y);
wheel.center = CGPointMake(wheel.center.x+spin, wheel.center.y);
//wheel.transform = CGAffineTransformMakeRotation(spin);
wheel.transform = CGAffineTransformRotate(wheel.transform, spin);
}
To rotate around an arbitrary point (i.e. not the origin):
shift the origin to the arbitrary point
perform rotation.
shift the origin back to original location.
(following link points to the math involved : http://www.euclideanspace.com/maths/geometry/affine/aroundPoint/matrix2d/)
Try the following code, this might solve your problem:
//translate the origin to the center of the wheel
wheel.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, wheel.center.x, wheel.center.y);
//rotate the wheel
wheel.transform = CGAffineTransformRotate(wheel.transform, spin);
//translate the origin back
wheel.transform = CGAffineTransformTranslate(wheel.transform,-wheel.center.x,-wheel.center.y);
//Move the wheel
wheel.transform = CGAffineTransformTranslate(wheel.transform,wheel.center.x+spin, wheel.center.y);
Try passing the angle irrespective of its current transformation like:
wheel.transform = CGAffineTransformMakeRotation( spin/180*M_PI );
I have a screen containing:
UIView A
UIScrollView, which contains UIView B
The UIScrollView overlaps with UIView A (up until about halfway through it), allowing me to "drag" UIView B and make it overlap it with UIView A. This is so that when I let go of it, UIView B bounces nicely back into its original position (I don't want to be able to drag it all around the screen, just from its starting point onto UIView A)
Now, I'm trying to detect a collision when the UIViews overlap. However, none is ever detected. I am using convertRect:toView: to get coordinates on the same system, but from tracing UIView B's coordinates in its touchesMoved I see that "dragging" (scrolling the UIScrollView, really) doesn't affect its frame's origin coordinates. They are the same in touchedBegan as they are every time touchedMoved fires.
I'm assuming this is because UIView B isn't really moving anywhere, UIScrollView is, meaning that the UIView's origin point is always the same, even if its absolute coordinates aren't. Any suggestion on how to handle this?
There will be overlap when scrollView.frame.origin.x + viewB.frame.size.width + scrollView.contentOffset.x >= viewA.frame.origin.x
After Edit:
I couldn't get the scroll view to work so I tried it another way that worked pretty well. Instead of a scroll view, I just used a rectangular view with a background color, and added the blue square to it. I gave the blue square a set width and height in IB, centered it in the long skinny view (in the y direction), and had one other constraint to the left side of that long view. The IBOutlet leftCon is connected to that constraint. I then used this code to drag it, and have it go back when I let go:
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface ViewController ()
#property (strong,nonatomic) UIPanGestureRecognizer *panner;
#property (strong,nonatomic) IBOutlet NSLayoutConstraint *leftCon;
#property (strong,nonatomic) CADisplayLink *displayLink;
#end
#implementation ViewController {
IBOutlet UIView *blueSquare;
}
-(void)viewDidLoad {
self.panner = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGesture:)];
[blueSquare addGestureRecognizer:self.panner];
}
- (void)handlePanGesture:(UIPanGestureRecognizer *)sender {
CGPoint translate = [sender translationInView:self.view];
if (self.leftCon.constant <160)
if (self.leftCon.constant > 100) NSLog(#"Bang! We have a collision");
self.leftCon.constant = translate.x;
if (sender.state == UIGestureRecognizerStateEnded){
[self startDisplayLink];
}
}
-(void)startDisplayLink {
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(bounceBack:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink {
[self.displayLink invalidate];
self.displayLink = nil;
}
-(void)bounceBack:(CADisplayLink *) link {
self.leftCon.constant -= 12;
if (self.leftCon.constant < 2) {
[self stopDisplayLink];
self.leftCon.constant = 2;
}
}
The numbers in the translate code (160 and 100) were determined empirically by logging. The 160 number keeps it from going off the right edge of the long view, and the 100 number is where it starts to overlap the black box.