So i am developing a game and have coded the score system. I want it to be so that when my object that is scrolling down is equal to the same y.co-ordinate as the "Box" object, the score system adds one to the score.
However, upon trying this the socre does not update and stays at 0. Im not quite sure why, could you please tell me where I may be going wrong and how i could fix it.
This is the code I am using to code the score system. Any help would be appreciated, thanks.
-(void)Score{
ScoreNumber = ScoreNumber + 1;
ScoreDisplay.text = [NSString stringWithFormat:#"%i", ScoreNumber];
}
[There is also some code in the view did all. but the problem is not with this part of the code as i have used it before and it works fine.]
This is where it is meant to be implemented but is not working...
if (ROne.center.y == IView.center.y) {
[self Score];
}
if (RTwo.center.y == IView.center.y) {
[self Score];
}
if (RThree.center.y == IView.center.y) {
[self Score];
}
if (RFour.center.y == IView.center.y) {
[self Score];
}
if (RFive.center.y == IView.center.y) {
[self Score];
}
if (RSix.center.y == IView.center.y) {
[self Score];
}
Your problem is likely that is the y-increment doesn't add up to the y-coordinate of the target object, then it won't be strictly equal. You'll just sort of pass by it. This is why >= worked, except once you passed it, it will keep adding to the score.
So, what you need to test for is "passing by". That is to say that prior to moving, ROne.center.y < IView.center.y and after moving ROne.center.y >= IView.center.y. That is the only condition that effectively check for "ROne just arrived at the desired height".
Alternatively, you can use >= and use a flag arrived on ROne to tell it to quit counting the score. Set the arrived flag to false when (ROne.center.y >= IView.center.y && ROne.arrived) is true. Then reset the arrived flag to true when you decide to reset the object to the top of the screen.
You need to think through the logic of your game code:
Some object is moving along the Y-axis each frame and that movement is likely to be more than 1 pixel (or point) per frame.
Your tick method is being called each frame (or some other set interval) and it's coded to only work when the Y-coordinate exactly matches the boundary you have set.
What's happening is the object is moving past the boundary between ticks and the method is therefore unable to detect it (i.e. think about a boundary where y == 3 and the object goes from y == 1 to y == 5 in a single tick).
What you need to do is hold a flag showing if the object has gone past the boundary and only check if the boundary condition if this flag is false. The best place to put this flag is in the object itself.
Something like:
if (!ROne.hasScored && ROne.center.y => IView.center.y) {
[self Score];
ROne.scored = YES;
}
You also need to consider using an array of the six objects, rather than 6 individual variables.
Related
I am encountering a very strange bug that I just can't figure out. In my game, you are driving a car on a 2D plane trying to avoid obstacles (like a rock or car). The obstacles can appear on 3 different paths. Sometimes there is just one obstacle on any of the paths or sometimes there is two obstacles on two of the three paths.
The problem I am experiencing is that when two obstacles appear and then get deleted when they go off the screen, one of the obstacles currently moving on the screen is also "deleted" (I know this because the console prints "deleted object" 3 times). But, the obstacle that is randomly "deleted" is not removed from view. Instead, it freezes where it previously was and never goes away.
Here is the code block I am having problems with:
var canGetScore = true
for (num,obj) in enumerate(obstaclesToMove) {
obj.position.y -= CGFloat(gameSpeed)
if obj.position.y <= userCar.position.y && obj.name == "NotPassed" {
obj.name = "Passed"
if canGetScore {
canGetScore = false
score += 1
scoreLabel.text = "\(score)"
}
}
if obj.position.y <= -CGRectGetMidY(self.frame) {
obj.removeFromParent()
obstaclesToMove.removeAtIndex(num)
println("deleted object")
}
}
Variable Key:
"obstaclesToMove" is an array of obstacles(SKSpriteNodes) that should be moved down the screen.
"gameSpeed" is an integer of how many pixels the screen moves every frame (the value is 5 in this instance).
"userCar" is the character that the user controls (SKSpriteNode).
The "canGetScore" thing is to eliminate another bug I was experiencing before. I know for a fact this is not causing the current bug I am having.
Does anyone have any ideas to why this is happening? If you need any more explanation just ask.
EXTRA NOTE: The user's car technically never moves, but the background and the obstacles move.
I had the same thing to implement in my game which is very similar to yours, so I guess that everything I've done can be applied to your situation. What I did instead of using indexes to remove obstacles from array of obstacles is that I've used addObject and removeObjectIdenticalTo methods(self.obstacles is NSMutableArray):
[self.obstacles addObject:asteroide]; //asteroide is subclass of SKSpriteNode
[self.obstacles addObject:enemyShip];//enemyShip is subclass of SKSpriteNode
And when I removing them, I just do something like:
[self.obstacles removeObjectIdenticalTo:asteroide];
[self.obstacles removeObjectIdenticalTo:enemyShip];
This worked for me... One more thing I would like to point is how you can remove obstacles when offscreen in a different way than yours. This is how I do that:
SKAction *sequence = [SKAction sequence:#[moveAsteroide,[SKAction runBlock:^{[asteroide removeFromParent]; [self.obstacles removeObjectIdenticalTo:asteroide];
}]]];
[asteroide runAction:sequence withKey:#"moving"];
So I run the sequence which first move object to desired location, and that is some point which is offscreen (y = -obstacle.size.height) and after that, I remove it from parent and update obstacles array. This works for me because I use actions to move obstacles, but maybe it can work for you if you are moving obstacles in same way. I like this more than constantly checking the location of obstacle in update method.
This is just my way though, and all this can be done in many ways, and you have to choose which one suits you better. Hope this make sense and helps a bit :-)
It seems to me that the problem is in how you remove your object from obstaclesToMove
Imagine you have three objects in your array:
obstaclesToMove = [object1, object2, object3]
You have to remove object 1 & 2 because they have to go off screen, so what you do in your code is first remove object 1 then object 2, except in your code it translates to :
obstaclesToMove.removeAtIndex(0)
obstaclesToMove.removeAtIndex(1)
You would think this isn't a problem, except look what happens to your array while you do that:
obstaclesToMove = [object1, object2, object3]
obstaclesToMove.removeAtIndex(0)
// obstaclesToMove = [object2, object3]
obstaclesToMove.removeAtIndex(1)
// obstaclesToMove = [object2]
Usually it is a bad idea to remove object from an array that you are looping through. What you could do to make it safer is:
Collect all the index you need to remove in an array
Sort them in reverse order
Delete the items from the array
You could do it like that:
var indexToDelete: [MyObstacleObject] = []
var canGetScore = true
for (num,obj) in enumerate(obstaclesToMove) {
obj.position.y -= CGFloat(gameSpeed)
if obj.position.y <= userCar.position.y && obj.name == "NotPassed" {
obj.name = "Passed"
if canGetScore {
canGetScore = false
score += 1
scoreLabel.text = "\(score)"
}
}
if obj.position.y <= -CGRectGetMidY(self.frame) {
indexToDelete.append(num)
}
}
indexToDelete.sort({ $0 > $1 })
for item in indexToDelete {
obstaclesToMove[item].removeFromParent()
obstaclesToMove.removeAtIndex(item)
println("deleted object")
}
That way when you delete items from your array you can be sure that they will still have the right index since you will be first deleting items with a higher index.
I'm using a SpriteKit game engine within XCode while developing a game that bounces a ball and platforms come from the sky and the objective is to bounce on the platforms to get higher. I need to add a velocity to the ball when it falls down + comes in contact with a platform. I'm having trouble trying to detect the balls Y position. I had something like this in the update method but nothing happens... I'm open to suggestions.
//The value of the _number instance variable is the Y position of the ball.
if (_number++) {
NSLog(#"UP");
}
if (_number--) {
NSLog(#"DOWN");
}
You can see a node's position by using code like this:
if(myNode.position.y > 0)
NSLog(#"y is greater than 0");
If you want to check a node's current speed (vector) you can do it like this:
if(myNode.physicsBody.velocity.dy > 0)
NSLog(#"Moving up");
if(myNode.physicsBody.velocity.dy < 0)
NSLog(#"Moving down");
Remember that position and speed (vector) are not the same thing.
You need to read the SKPhysicsBody docs to understand about CGVector and other important issues.
Alright, this:
if (_number++) {
NSLog(#"UP");
}
if (_number--) {
NSLog(#"DOWN");
}
doesn't work to do anything. It shouldn't even run. I don't why it doesn't give an error.
number++; is a simplified form of
number = number + 1;
You can't place it in an if statement.
First make the variable #property int ypos in your header.
Then every update compare the current y to the ypos.
After comparing, save the current y to ypos.
Place this in your Update method.
if (ball.position.y > _ypos)
{
//going up
}
else if (ball.position.y < _ypos)
{
//going down
}
_ypos = ball.position.y;
P.S. I haven't had access to a computer so the formatting might suck from my phone
i have a game where the user must move the ball in a vertical way between 2 objects , i want to have a scoring system that allows me to add 1 to the score each time the ball is equal to the 2 objects so i tried the code below but it is not working probably,it is not adding one each time the ball's y coordinates are equal to the object's y cooridnates.
if(imageview.center.y == imageview1.center.y){
int score = score + 1;
scorelabel.text = [NSString stringWithFormat:#"%i",score];
}
As I assume your imageview.centre.y will be different then imageView1.centre.y .Also you could use this method CGRectIntersection(<#CGRect r1#>, <#CGRect r2#>) and place your both frame in provided fields. So it will notify whenever there is an intersection of two frames. Code will be like -
if (CGRectIntersectsRect(self.pacman.frame, self.exit.frame))
{
//Wirte your score handling code here...
}
else
{
//Skip or do what you want...
}
Hope this might help you.
Your variable is recreated every time your if is performing.
Define it somewhere before #implementation
int score = 0;
And replace int score = score + 1; with score++
As comment you should be aware of comparison of double. See This Question for details
I already know how to check for collisions with the doesintersectNode-method in Cocos3d, but in my case, I want to avoid obstacles, before I get in touch with them. In example, I want to stop in front of a wall, before I crash against it.
For this reasons I wrote the methods getNodeAtLocation in my subclass of CC3Scene and -(BOOL)shouldMoveDirectionallywithDistance:(float)distance in the class of my person, which should move around.
Unfortunately, I have some problems with the algorithm of the last method. Here the code:
-(BOOL)shouldMoveDirectionallywithDistance:(float)distance
{
BOOL shouldMove = NO;
float x = self.person.globalLocation.x;
float z = self.person.globalLocation.z;
int times = 5;
for (int i = 0; i < times; i++) {
CC3Vector newPos = cc3v(x, 0.5, z);
CC3PODResourceNode *obstacle = (CC3PODResourceNode *)[myScene getNodeAtLocation:newPos];
if (obstacle) {
return NO;
}else{
shouldMove = YES;
}
x += self.person.globalForwardDirection.x * distance / times;
z += self.person.globalForwardDirection.z * distance / times;
}
return shouldMove;
}
In this method, I get the important parts of the coordinates (for my proposal just the x- and z-values) and increase them by a fifth of the forwardDirection. I decided, that this makes sense, when the obstacle is i.e. a thin wall. But for reasons I don't know, this method doesn't work, and the person is able to walk through this wall. So where is the problem in my code?
I strongly believe, that the getNodeAtLocation-method works correctly, as I tested it multiple times, but maybe there are my mistakes:
-(CC3Node *)getNodeAtLocation:(CC3Vector )position
{
CC3Node *node = nil;
for (CC3PODResourceNode *aNode in self.children) {
if ([aNode isKindOfClass:[CC3PODResourceNode class]] ) {
for (CC3PODResourceNode *child in aNode.children) {
if (CC3BoundingBoxContainsLocation(child.globalBoundingBox, position)) {
node = aNode;
}
}
}
}
return node;
}
To conclude, in my view the mistake is in the -(BOOL)shouldMoveDirectionallywithDistance:(float)distance-method. I suppose, that something is wrong with the increase of the x- and z-values, but I couldn't figure out, what exactly is incorrect.
If you are still interested in finding an answer to this problem. I may be able to provide you with an alternative solution. I am about to release free source for a 3d collision engine I ported to cocos3d, and it will give you more flexibility than simply stoping an object in front of another.
I am currently polishing out the code a little for easy use, but if you are interested you can email me to: waywardson07#aol.com
you could also get a little preview of the engine in action here: http://www.youtube.com/watch?v=QpYZlF7EktU
Note: the video is a little dated.
After several attempts, it appears to be easier as I thought:
Just using the doesIntersectNode method has the right effect for me.
But please notice, that this is not a real solution to the problem of stopping in front of obstacles.
I have an image that the user can drag to the right and it will spring back when the user releases it. I want to execute some code when a user drags it quickly and releases it. Now I have a very awkward requirement that the user can drag the image, then keep it still for any length of time (for example 5 seconds), then drag it quickly and release it. As long as the image is moving above a certain speed when it is released, it will execute the code. If it falls below the minimum speed, it executes some different code. So that means I can't calculate the length of time between the beginning of the gesture and the end and execute the code depending on the length of time. What can I do? I guess I somehow need to know the speed at which the image is moving in it's last 500 milliseconds before the gesture ends. However I've hit a brick wall figuring out how to do that. Any help would be greatly appreciated.
Can you please include an explanation and possible example code with your answer as that would be a great help.
If you get the start X,Y coordinates of when the image is dragged, and the X,Y coordinates for when the mouse is released, you can use pythagoras' theorm to calculate the distance between the two points: http://en.wikipedia.org/wiki/Pythagorean_theorem
Also, if you start a timer when the mouse is moved (and mouse button is down), and stop it in the mouseup event, you can calculate the speed using the time and distance (speed = distance / time)
edit following comments:
point delayedMousePos;
point previousMousePos;
bool secondDrag = false;
bool isStopped = false;
var timeFirstStopped;
var positionCount = 0;
array previousMousePositions[3];
// timer which monitors mouse position (set to an interval of say, 10ms)
function timerMonitorMousePos_Elapsed() {
point currentMousePos = getMousePos();
if (isStopped == false) {
if (positionCount >= 2) {
array_shift(previousMousePositions); // remove the first element of the array and move everything down to reindex numerical array to start counting from zero
positionCount = 2; // keep positionCount within array bounds
}
previousMousePositions[positionCount] = currentMousePos; // add the new position to the end of the 'stack'
positionCount++;
}
if (currentMousePos == previousMousePos) { // start check for stationary
isStopped = true;
if (timeFirstStopped == null) {
timeFirstStopped = NOW();
} else {
if (NOW() - timeFirstStopped >= 500) { // we have been stopped for at least 500ms (assumes time is counted in milliseconds)
secondDrag = true;
// previousMousePositions[0] = the mouse position 30ms before the mouse stopped
}
}
} else {
isStopped = false;
timeFirstStopped = null;
}
previousMousePos = currentMousePos;
}
I wouldn't use a timer. I would just save the starting date/time along with x,y position when the dragging starts.
When the dragging has ended, save the ending date/time and position. From those information, I can calculate the distance in pixel and duration in milliseconds.
After searching some more on the internet, I finally answered my own question.
I worked out what I needed to do:
My UIPanGestureRecognizer:
- (IBAction)handlePan3:(UIPanGestureRecognizer *)recognizer3
Get the velocity of the users finger moving across the screen:
CGPoint vel = [recognizer velocityInView:self.myView];
Then:
if (vel.x > /*value*/) {
// Your code
}
I was about to give up, but no! I got there in the end. Thanks for everyones help. I've upvoted one or two answers because they were helpful. bobnoble actually gave the suggestion to use velocityInView and I found this other stack overflow question which gave me the info I needed: iOS - Making sense of velocityInView on UIPanGesture