i am doing a single view game demo, a maze like game with walls. player will control the character with a UIButton.
basically i have devised that i will need to check that if in the next step, the character is going to intersect with a wall, the character movement will return false. but, i cannot seem to put them together.
i currently have this boolean function to check if the character is going to intersect with the wall
-(Boolean) checkCollision : (CGRect) newFrame{
CGRect frame = self.mainchar.frame;
frame.origin.x = self.currentPoint.x;
frame.origin.y = self.currentPoint.y;
for (UIImageView *image in self.hardwalls) {
if (CGRectIntersectsRect(frame, image.frame)) {
return true;
}
}
return false;
}
my UIButton for movement is
-(IBAction)CharMovingLeft:(id)sender; {
CGPointMake(mainchar.center.x -charmovement, mainchar.center.y);
what should i be adding into the button method so that it will stop this movement when the intersection is going to happen?
thanks in advance
-(IBAction)CharMovingLeft:(id)sender {
CGRect newFrame = YOUR_CHARACTERS_FRAME_IF_IT_MOVED;
if (![self checkCollision:newFrame];
//MOVE CHARACTER:
}
else
//DONT MOVE CHARACTER
}
Will this do ... ?
Related
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'm a beginner at objective C learning to program, and also beginner at asking questions on this site, please bear with me.
I am currently trying to draw a column of boxes (UIControls) on the screen, and be able to scroll them upward or downward infinitely. So when one goes off the bottom of the screen its shifted to the bottom and reused.
I know there must be a lot of mistakes in the code. But the gist of what I am trying to do is: The boxes are all in an array (imArray). When a box scrolls off the bottom of the screen its taken off the end of the array, and inserted at the beginning. Then the box inserts itself graphically into the top of the column.
The first if statement deals with scrolling off the bottom of the screen, and it works fine. But the second if statement, where i try to do the opposite with similar code works only when i scroll slowly, when i scroll quickly the spacing between boxes becomes uneven, and sometimes a box just locks up on the screen and stops moving.
Any help is appreciated, and I will try to provide any more clarity that may be needed.
-(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint pt = [touch locationInView:self];
int yTouchEnd = pt.y;
int yTouchChange = yTouchEnd - yTouchStart;
//iterate through all boxes in imArray
for(int i = 0; i < self.numberOfSections; i++)
{
//1. get box
STTimeMarker *label = self.imArray[i];
//2. calculate new label transform
label.transform = CGAffineTransformTranslate(label.startTransform, 0, yTouchChange);
CGRect frame = label.frame;
//3. if the box goes out of the screen on the bottom
if (frame.origin.y > [[UIScreen mainScreen]bounds].size.height)
{
//1. move box that left the screen to to beginning of array
[self.imArray removeObjectAtIndex:i];
[self.imArray insertObject:label atIndex:0];
//2. get y value of box closest to top of screen.
STTimeMarker *labelTwo = self.imArray[1];
CGRect frameTwo =labelTwo.frame;
//3. put box that just left the screen in front of the box I just got y value of.
frame.origin.y = frameTwo.origin.y - self.container.bounds.size.height/self.numberOfSections;
label.frame=frame;
}
//1. if the box goes out of the frame on the top
// (box is 40 pixels tall)
if (frame.origin.y < -40)
{
[self.imArray removeObjectAtIndex:i];
[self.imArray addObject:label];
STTimeMarker *labelTwo = self.imArray[self.numberOfSections-1];
CGRect frameTwo =labelTwo.frame;
frame.origin.y = frameTwo.origin.y + self.container.bounds.size.height/self.numberOfSections;
label.frame=frame;
}
}
return YES;
}
If I understand what you are trying to do correctly, I think you want to come at this a different way. Your data model (the array) does not need to change. All that is changing as you scroll is the view, what is displaying on screen. The simplest way to achieve the appearance of an infinite scroll would be to use a UITableView and give it some large number of cells. Then your cellForRowAtIndexPath: method will return the cell for the correct position using the mod operator (%). Untested code:
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
return 99999;
}
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
NSInteger moddedRow = indexPath.row % [self.imArray count];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kSomeIdentifierConst forIndexPath:[NSIndexPath indexPathForRow:moddedRow inSection:indexPath.section]];
return [self configureCellWithData:self.imArray[moddedRow]];
}
This may not be sufficient for your purposes if you need true infinite scroll, but should work for most purposes.
I am creating a puzzle game for which you have to move an object around obstacles in order to reach your target. However, for some reason objects are overlapping when I use CGRectIntersectsRect. I want the objects to STOP when they touch edges with each other, NOT when they're overlapping each other. Current code is as follows:
-(void)objectObstacleCollision {
if (CGRectIntersectsRect(object.frame, obstacle1.frame)) {
xMotion = 0;
yMotion = 0;
if (objectMovingUp == YES) {
objectCrashedUp = YES;
objectMovingUp = NO;
if (objectCrashedUp == YES && objectMovingUp == NO) {
up.hidden = YES;
down.hidden = NO;
right.hidden = NO;
left.hidden = NO;
}
}
This is causing objects to overlap upon impact which causes problems when trying to move the object in a different direction. After many different attempts, for the life of me, I cannot get the object to stop when it touches edges with obstacles. How can I get this to happen?
If two rects share an edge, they don't intersect, they touch. For example, this code:
CGRect rect1 = CGRectMake(0, 0, 100, 100);
CGRect rect2 = CGRectMake(0, 100, 100, 100);
if (CGRectIntersectsRect(rect1, rect2)) {
NSLog(#"The intersection rect is %#", NSStringFromCGRect(CGRectIntersection(rect1, rect2)));
} else {
NSLog(#"The rects don't intersect.");
}
will output "The rects don't intersect."
There's no built-in CGRect function to determine if two rects are touching, but you could write one that iterates through the 4 possibilities.
I have looked up all related questions to this question but nothing brought success.
The problem is probably pretty simple:
I want to detect a collision between two geometrical objects which are animated in a way they are hitting eachother at a given time.
In order to use the method CGRectIntersectsRect i have to init the UIImage excactly with the size of the actual image of the geometrical object so it returns only a collision if the images/frames really collide.
So how do i specify the postion to place my image i have intiliazid with initWithImage.
I can't use the Method initWithFrame:CGRectMake as i do not want a UIImageview shaped as a rectangle.
Examples which do not work:
circleYellow = [[UIImageView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height / 2, 50, 50)];
circleYellow.image = [UIImage imageNamed:images[arc4random() % 5]];
circleYellow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:images[arc4random() % 5]]];
I would recommend you check out cocos2d. It has tools for handling sprites, updating their position through Actions (animations) on the screen, etc.
Detecting the collision of two sprites becomes something like:
-(void)update(float dt)
{
CGRect rect1 = [sprite1 boundingBox];
CGRect rect2 = [sprite2 boundingBox];
if (CGRectIntersectsRect(rect1, rect2))
{
// Do something about the collision...
}
}
And if you need more sophisticated modeling of the motion, or if you want to be able to collide arbitrary shapes and not just the bounding boxes, Box2D built in. There are lots of examples available for this as well.
Was this helpful?
UIImageView and it's superclass UIView always create rectangles. From the UIView class reference:
The UIView class defines a rectangular area on the screen ...
It sounds like SpriteKit may be what you need - check out the sprite kit programming guide.
I would recommend using the SpriteKit:
//Create a SpriteNode loading your image
SKSpriteNode *cicleYellow = [SKSpriteNode spriteNodeWithImageNamed:images[arc4random() % 5]];
//Set the size and the position of the Sprite
circleYellow.size = CGSizeMake(50,50);
circleYellow.position = CGPointMake(0, self.view.frame.size.height);
//Create a circlePhisicsBody and add it to the Sprite
SKPhysicsBody *circlePhisicsBody = [SKPhysicsBody bodyWithCircleOfRadius:cicleYellow.size.width/2.0f];
cicleYellow.physicsBody = circlePhisicsBody;
Check out the Apple documentation on how to use the Sprite Kit.
You would need some timer function to check that every now and then but the algorihm for detecting collisions among Rectangles is pretty straight forward (square A and square B):
//If any of the sides from A are outside of B
if( bottomA <= topB ) { return false; }
if( topA >= bottomB ) { return false; }
if( rightA <= leftB ) { return false; }
if( leftA >= rightB ) { return false; }
//If none of the sides from A are outside B return true;
If you need to do it for circles only here is a good tutorial: http://gamedevelopment.tutsplus.com/tutorials/when-worlds-collide-simulating-circle-circle-collisions--gamedev-769
I'm having trouble getting a repeatable background to work in my game menu.
The user can slide a finger across the screen to select a character to play.
I have a parallax effect working with various backgrounds as the characters slide into view.
Sample below.
- (void)didMoveToView:(SKView *)view
{
self.pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(dragScene:)];
self.pan.minimumNumberOfTouches = 1;
self.pan.delegate = self;
[self.view addGestureRecognizer:self.pan];
}
- (void)dragScene:(UIPanGestureRecognizer *)gesture
{
CGPoint trans = [gesture translationInView:self.view];
SKAction *moveSky = [SKAction moveByX:trans.x*0.03 y:0 duration:0];
[_skyBackground runAction:moveSky];
}
I would like to repeat the backgrounds. I know how to do this with automatically scrolling backgrounds but I can't seem to get it to work here. It needs to repeat in both directions, left and right.
Thanks for any help!
You can create two more background nodes - one to the left of your current background node and one to the right. Move them aswell any time you move your existing _skyBackground node.
Then, in the update method, check if any of the three nodes needs to be "shifted" - either to behind the other two or in front. You're basically swapping the three nodes' positions if needed.
-(void)update:(NSTimeInterval)currentTime {
//get the left background node (or if using an ivar just use _leftNode)
SKSpriteNode *leftNode = (SKSpriteNode*)[self childNodeWithName:#"leftNode"];
//my positioning might be off but you'll get the idea
if (leftNode.position.x < -leftNode.size.width*2)
{
leftNode.position = CGPointMake(leftNode.size.width, leftNode.position.y);
}
if (leftNode.position.x > leftNode.size.width*2)
{
leftNode.position = CGPointMake(-leftNode.size.width, leftNode.position.y);
}
//repeat the same for _skyBackground and _rightNode
}
You may need more than 3 images if there's a slight gap between images as they're shifted.