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.
Related
I'm using iOS charts framework to plot this chart, I want to detect tap or touch only on the line's path or on the small circle's on the lines.
My question is,
Is there any default code block to do this?
I tried comparing the entry.value with the array plotted(as in the following code), but it doesn't workout.
-(void)chartValueSelected:(ChartViewBase *)chartView entry:(ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(ChartHighlight *)highlight{
if ([arrayOfPlottedValues containsObject:[NSNumber numberWithInt:(int)entry.value]]) {
//Tapped on line path
}
else{
//Tapped on empty area
}
}
Any insights will be appreciated.
eg : Line chart
I found a way by considering #Wingzero's suggestion, but the major difference was that, I just used the touch point to find out if its on the "marker" or if its outside it. I'm not sure if its the right way, but the solution is,
-(void)chartValueSelected:(ChartViewBase *)chartView entry:(ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(ChartHighlight *)highlight{
//-----------------------------------------------------getting recognizer value
UIGestureRecognizer *recognisedGesture = [chartView.gestureRecognizers objectAtIndex:0];
CGPoint poinOfTouch =[recognisedGesture locationInView:chartView];
CGPoint poinOfMarker =[chartView getMarkerPositionWithEntry:entry highlight:highlight];
if (check if the chartview is BarChartView and if true) {
//-----------------------------------------------------If you want to detect touch/tap only on barchartview's bars
if (poinOfTouch.y > poinOfMarker.y) {
NSLog(#"within the bar area!");
}
else{
NSLog(#"Outside the bar area!");
}
}
else
{
//-----------------------------------------------------If you want to detect touch/tap only on linechartView's markers
//-----------------------------------------------------creating two arrays of x and y points(possible nearby points of touch location)
NSMutableArray *containingXValue = [[NSMutableArray alloc]init];
NSMutableArray *containingYValue = [[NSMutableArray alloc]init];
for (int i =0 ; i<5; i++) {
int roundedX = (poinOfMarker.x + 0.5);
int sumXValuesPositive = roundedX+i;
[containingXValue addObject:[NSNumber numberWithInt:sumXValuesPositive]];
int sumXValuesNegative = roundedX-i;
[containingXValue addObject:[NSNumber numberWithInt:sumXValuesNegative]];
int roundedY = (poinOfMarker.y + 0.5);
int sumYValuesPositive = roundedY+i;
[containingYValue addObject:[NSNumber numberWithInt:sumYValuesPositive]];
int sumYValuesNegative = roundedY-i;
[containingYValue addObject:[NSNumber numberWithInt:sumYValuesNegative]];
}
//-----------------------------------------------------------------------------------------------------------------------------------------
int roundXPointTOuched = (poinOf.x + 0.5);
int roundYPointTOuched = (poinOf.y + 0.5);
//-----------------------------------------------------check if touchpoint exists in the arrays of possible points
if ([containingXValue containsObject:[NSNumber numberWithInt:roundXPointTOuched]] && [containingYValue containsObject:[NSNumber numberWithInt:roundYPointTOuched]])
{
// continue, the click is on marker!!!!
}
else
{
// stop, the click is not on marker!!!!
}
//-----------------------------------------------------------------------------------------------------------------------------------------
}
}
}
Edit : The initial solution was applicable only for the line chart, Now if this same situation arises for bar chart, you could handle it with the above code itself.
Man, I'd been running around it for a while now, feeling really great for getting a positive lead. There is no direction for this issue yet, hope this will be useful for someone like me cheers!
P.S. I'm marking this as answer just to make sure, it reaches the needful :). Thanks
It has a default highlight logic, that is, calculate the closest dataSet and xIndex, so we know which data to highlight.
You can customize this logic to restrain the allowed smallest distance. e.g. define the max allowed distance is 10, if the touch point is away from the closest dot > 10, you return false and not highlgiht.
Highlighter is a class, like BarChartHighlighter, ChartHighlighter, etc.
Update towards your comment:
when you tapped, the delegate method get called, so you know which data is highlighted. Your codes seems fine, however the condition code is blackbox to me. But the delegate will be called for sure, so you only have to worry about your logic.
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
What would be the best way to do this? I see the CCEaseSineInOut action but it doesn't look like that could be used to do this.
I need to move from one side of the screen to the other. The sprite should move in a sine-wave pattern across the screen.
I always like to have complete control over CCNode motion. I only use CCActions to do very basic things. While your case sounds simple enough to possibly do with CCActions, I will show you how to move a CCNode according to any function over time. You can also change scale, color, opacity, rotation, and even anchor point with the same technique.
#interface SomeLayer : CCLayer
{
CCNode *nodeToMove;
float t; // time elapsed
}
#end
#implementation SomeLayer
// Assumes nodeToMove has been created somewhere else
-(void)startAction
{
t = 0;
// updateNodeProperties: gets called at the framerate
// The exact time between calls is passed to the selector
[self schedule:#selector(updateNodeProperties:)];
}
-(void)updateNodeProperties:(ccTime)dt
{
t += dt;
// Option 1: Update properties "differentially"
CGPoint velocity = ccp( Vx(t), Vy(t) ); // You have to provide Vx(t), and Vy(t)
nodeToMove.position = ccpAdd(nodeToMove.position, ccpMult(velocity, dt));
nodeToMove.rotation = ...
nodeToMove.scale = ...
...
// Option 2: Update properties non-differentially
nodeToMove.position = ccp( x(t), y(t) ); // You have to provide x(t) and y(t)
nodeToMove.rotation = ...
nodeToMove.scale = ...
...
// In either case, you may want to stop the action if some condition is met
// i.e.)
if(nodeToMove.position.x > [[CCDirector sharedDirector] winSize].width){
[self unschedule:#selector(updateNodeProperties:)];
// Perhaps you also want to call some other method now
[self callToSomeMethod];
}
}
#end
For your specific problem, you could use Option 2 with x(t) = k * t + c, and y(t) = A * sin(w * t) + d.
Math note #1: x(t) and y(t) are called position parameterizations. Vx(t) and Vy(t) are velocity parameterizations.
Math note #2: If you have studied calculus, it will be readily apparent that Option 2 prevents accumulation of positional errors over time (especially for low framerates). When possible, use Option 2. However, it is often easier to use Option 1 when accuracy is not a concern or when user input is actively changing the parameterizations.
There are many advantages to using CCActions. They handle calling other functions at specific times for you. They are kept track of so that you can easily pause them and restart them, or count them.
But when you really need to manage nodes generally, this is the way to do it. For complex or intricate formulas for position, for example, it is much easier to change the parameterizations than to figure out how to implement that parameterization in CCActions.
I'm trying to make some moving tiles from a Tiled map editor tmx file.
I have the moving tiles in their own layer, and I just want to simply have them move up, and then when they reach a certain y, move back down, and etc.
I have been looking around for a bit on a clear way of accomplishing this, but my efforts have been unsuccessful.
I tried using some of the methods here.
I'm still really new to cocos2d development in general, so I wold appreciate any insight on this. Thank you very much for your time. If you have any questions, please ask! :)
Also if it helps, the tiles I'm trying to move are in a big T shape.
FINAL UPDATE:
(Removed more irrelevant code so anyone in the future can easily find my solution (the full answer is below), you can find where I got my layer iterate method at the link above).
Okay, so I have finally got it working close to how I want.. I don't think this is exactly the most ideal way of doing it, but this is what I've got.
Note: In order for this to work for you, you have to run your app out of debug mode or it will lag/make the player fall through the ground (at least it did for me..).
I have an update function that calls certain functions every frame. (Checking collisions, moving platforms, etc).
That update function calls my move platforms function..
like this:
[self movePlatforms:0.1];
this is my movePlatforms function..
-(void)movePlatforms: (ccTime) dt{
if(goingDown){
moveCount++;
}else{
moveCount--;
}
CGSize s = [movingTiles layerSize];
for( int x=0; x<s.width;x++) {
for( int y=0; y< s.height; y++ ) {
CCSprite *tile = [movingTiles tileAt:ccp(x,y)];
if(goingDown){
CGPoint newPosition = ccp(tile.position.x, tile.position.y - 1);
tile.position = newPosition;
if(moveCount >= 100){
goingDown = false;
}
}else{
CGPoint newPosition = ccp(tile.position.x, tile.position.y + 1);
tile.position = newPosition;
if(moveCount <= 0){
goingDown = true;
}
}
}
}
}
So basically, I created a int moveCount and a BOOL goingDown to keep track of how many times my movePlatform function has been called. So after 100 calls, it switches direction.
(This works fine for me, you might need something else like a collision detecter if that is the case use this).
if (CGRectIntersectsRect([someSprite boundingBox], [someSprite boundingBox])) {
//Do something
}
Hopefully this works for someone in the future, I know this was quite the headache for me, and it probably isn't even done correctly or there is a much better way to do it, but if this helps you, that is awesome!
Creating and removing tiles will effect your performance.
Instead of it, try to move the tile changing their position:
CCSprite *tile = [movingTiles tileAt:ccp(92,platformY)];
[movingTiles removeTileAt:ccp(92,platformY)];
CGPoint newTilePosition = tile.position;
if (goingDown){
newTilePosition.y ++;
if(newTilePosition.y >= 20){
goingDown = false;
}
}else{
newTilePosition.y --;
if(newTilePosition.y <= 10){
goingDown = true;
}
}
tile.position = newTilePosition;
Here is the (kind of) step by step of how I got my moving tiles working, this is only related to the moving tiles, and nothing else.
Note: You will need to run this as a release (not debug) in order to get everything running smoothly, and not having your character fall through the ground.
In the interface I created these variables:
#interface HelloWorldLayer(){
CCTMXTiledMap *map;
BOOL goingDown;
int moveCount;
}
The CCTMXTiledMap is the instance of my map.
The BOOL and int are two variables I use to keep track of my moving tiles.
-(id) init {
if( (self=[super init]) ) {
// add our map
map = [[CCTMXTiledMap alloc] initWithTMXFile:#"level1-1.tmx"];
map.position = ccp(0,0);
[self addChild:map];
//add our moving platforms layer
movingTiles = [map layerNamed:#"moving_platforms"];
//set the variables I use to keep track of the moving platforms
goingDown = true;
moveCount = 0;
//schedule my update method
[self schedule:#selector(update:)];
}
return self;
}
After the init method, I then create my move platforms method:
-(void)movePlatforms: (ccTime) dt{
if(goingDown){
moveCount++;
}else{
moveCount--;
}
CGSize s = [movingTiles layerSize];
for( int x=0; x<s.width;x++) {
for( int y=0; y< s.height; y++ ) {
CCSprite *tile = [movingTiles tileAt:ccp(x,y)];
if(goingDown){
CGPoint newPosition = ccp(tile.position.x, tile.position.y - 1);
tile.position = newPosition;
if(moveCount >= 100){
goingDown = false;
}
}else{
CGPoint newPosition = ccp(tile.position.x, tile.position.y + 1);
tile.position = newPosition;
if(moveCount <= 0){
goingDown = true;
}
}
}
}
}
So this is where the magic happens, I use methods I got from here, and the gentleman Mauricio Tollin told me I could update a tile position rather than destroy and recreate them.
So I iterate through every tile in my moving platforms layer, and tell them to go down 1 every call, until moveCount >= 100, then it says goingDown is now false, and it switches its direction. From there it just goes back and forth, counting to 100, and then back down.
If you want it to move longer, just increase 100 to 200 or whatever you want. (Or you can use a check to detect collision, and when it collides with a specified sprite, you can have it change then. If that is more of what you want, use this).
if (CGRectIntersectsRect([someSprite boundingBox], [someSprite boundingBox])) {
//Do something
}
After all of that, I create my update method:
-(void)update:(ccTime)dt{
[self movePlatforms:0.1];
}
In the init method it schedules the update method to be called every frame, and then the update method will run the movePlatforms method (or any other function that needs to be checked frequently, such as hazard detection, etc).
You can also make the platforms move slower by changing the time passed into movePlatforms, or you can schedule a slower update interval in the init method.
I hope this helps someone out in the future, I just wanted to create this answer with a more in depth process of how I got this working, since my question post was really unorganized and heavily edited while I was learning.
heres the problem
i have 5 balls floating around the screen that bounce of the sides, top and bottom. thats working great.
what i want to do now is work out if any of them collide with each other.
i know about
if (CGRectIntersectsRect(image1.frame, image2.frame))
{
}
but that only checks two images, i need to check all and each of them..
ive checked everywhere but cant find the answer, only others searching the same thing, any ideas?
thanks in advance
Spriggsy
edit:
im using this to find the CGRect and store it in an array
ball1 = NSStringFromCGRect(image1.frame);
ball2 = NSStringFromCGRect(image2.frame);
ball3 = NSStringFromCGRect(image3.frame);
ball4 = NSStringFromCGRect(image4.frame);
ball5 = NSStringFromCGRect(image5.frame);
bingoarray = [NSMutableArray arrayWithObjects:ball1,ball2,ball3,ball4,ball5,nil];
this then gets passed to a collision detection method
-(void)collision {
for (int i = 0; i<[bingoarray count]-1 ; i++) {
CGRect ballA = CGRectFromString([bingoarray objectAtIndex:i]);
if (CGRectIntersectsRect(ballA, image1.frame)) {
NSLog(#"test");
}
}
this i guess should check one ball against all the others.
so ball 1 gets checked against the others but doesnt check ball 2 against them. is this nearly there?
}
The ideal solution is to store all the rectangles into a interval tree or a segment tree in order to efficiently compute any overlapping areas. Note that you will have to generalize to 2 dimensions for your use case.
Another efficient approach would be to use a K-d tree to find the nearest other balls and compare against the nearest neighbor until there isn't a collision.
The simple approach is to simply iterate over all the balls and compare them to all other balls with a higher ID (to avoid double checking ball1 -> ball2 and ball2 -> ball1).
Since you only have 5 at once, the iterative approach is likely fast enough to not be dropping frames in the animation, but you should consider a more scalable solution if you plan to support more balls since the simple appreach run in quadratic time.
That is a fun little math problem to avoid being redundant.
You can create an array of the images. And loop through it, checking if each member collides with any successive members.
I can spell it out more with code if need be.
EDIT I couldn't resist
// the images are in imagesArray
//where you want to check for a collision
int ballCount = [imagesArray count];
int v1Index;
int v2Index;
UIImageView * v1;
UIImageView * v2;
for (v1Index = 0; v1Index < ballCount; v1Index++) {
v1 = [imagesArray objectAtIndex:v1Index];
for (v2Index = v1Index+1; v2Index < ballCount; v2Index++) {
v2 = [imagesArray objectAtIndex:v2Index];
if (CGRectIntersectsRect(v1.frame, v2.frame)) {
// objects collided
// react to collision here
}
}
}