I want cocos2d to use the nodes' position as a normalized factor when coordinates are less than 1, and otherwise when coordinates are greater than 1, I want cocos2d to treat coordinates as it would normaly do
For instace position (1,1) would locate the node at the top-right corner of the screen, while (.5,.5) would locate the node on the center of the screen.
I was thinking on rewriting the code just where cocos2d pass on the coordinates to the vertex buffer like the following pseudo code
if (Position.x <= 1)
BufferPosition.x = Screen.Width * Position.x;
else
BufferPosition.x = Position.x;
if (Position.y <= 1)
BufferPosition.y = Screen.Height * Position.y;
else
BufferPosition.y = Position.y;
So my question is where would you put this code so that cocos2d works that way with no problem, or what approach would you take istead if it's different from the shown above
Update
I changed every single call/assignment of positionInPixels_ in the entire cocos2d library for positionInPixels
This means that each time, the corresponding getter and setter is caled instead of the CCNode's positionInPixels_ member variable.
Then I changed setPosition, and setPositionInPixels in CCNode.m like this
setPosition
-(void) setPosition: (CGPoint)newPosition
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
position_ = newPosition;
if (position_.x <= 1.0) {
positionInPixels_.x = position_.x * winSize.width;
}
else
{
if( CC_CONTENT_SCALE_FACTOR() == 1 )
positionInPixels_.x = position_.x;
else
positionInPixels_.x = newPosition.x * CC_CONTENT_SCALE_FACTOR();
}
if (position_.y <= 1.0) {
positionInPixels_.y = position_.y * winSize.height;
}
else
{
if( CC_CONTENT_SCALE_FACTOR() == 1 )
positionInPixels_.y = position_.y;
else
positionInPixels_.y = newPosition.y * CC_CONTENT_SCALE_FACTOR();
}
isTransformDirty_ = isInverseDirty_ = YES;
#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
isTransformGLDirty_ = YES;
#endif
}
setPositionInPixels
-(void) setPositionInPixels:(CGPoint)newPosition
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
positionInPixels_ = newPosition;
if (position_.x <= 1.0) {
position_.x = positionInPixels_.x / winSize.width;
}
else
{
if( CC_CONTENT_SCALE_FACTOR() == 1 )
position_.x = positionInPixels_.x;
else
position_.x = newPosition.x * 1/CC_CONTENT_SCALE_FACTOR() / winSize.width;
}
if (position_.y <= 1.0) {
position_.y = positionInPixels_.y / winSize.height;
}
else
{
if( CC_CONTENT_SCALE_FACTOR() == 1 )
position_.y = positionInPixels_.y;
else
position_.y = newPosition.y * 1/CC_CONTENT_SCALE_FACTOR() / winSize.height;
}
isTransformDirty_ = isInverseDirty_ = YES;
#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
isTransformGLDirty_ = YES;
#endif
}
This is working so far, even when using normalized coordinates with CCActionInterval
So you really think that this variant is the right one? To change the whole framework instead of just adding one method to the CCNode's extension? I think you will see a lot of problem with positioning very soon.
Also it is not good, that (1.f, 1.f) is always have coordinates (screenWidth, screenHeight). You will have a lot of troubles with small nodes. IMHO if you need such positioning, it is much more appropriate to connect this "factor" coordinates to the node's parent size.
Just do restore cocos2d framework and add your own extension to your project. For example,
#interface CCNode(FactorPositioning)
- (void) setPositionFactor:(CGFloat) factor;
#end
#implementation CCNode(FactorPositioning)
- (void) setPositionFactor:(CGFloat) factor
{
assert(self.parent != nil);
// do your calculations relatieve to the parent's content size
}
#end
Related
How do we enumerate and alter our object's position (contained in array) for each delta time?
I put some CCsprite objects inside array, then I displayed them in scene, but also I wanted to make them move with modifying update method, I failed on last part.
How do I get around this ?
#implementation GameScene
{
Hunter *_hunter;
Bird *_bird;
NSMutableArray *_arrayOfBirds;
}
-(void)update:(CCTime)delta{
CGSize viewSize = [CCDirector sharedDirector].viewSize;
float birdSpeed = 50;
for (Bird *birds in _arrayOfBirds) {
if (birds.position.x < 0) {
birds.flipX = YES;
}
if (birds.position.x > viewSize.width) {
birds.flipX = NO;
}
float distanceToMove = birdSpeed * delta;
float direction = birds.flipX ? 1 : -1;
float newX = birds.position.x + direction * distanceToMove;
float newY = birds.position.y;
birds.position = ccp(newX, newY);
}
}
-(void)addBird{
CGSize viewSize = [CCDirector sharedDirector].viewSize;
for (int i=0; i < 4; i++) {
_bird = [[Bird alloc]initWithBirdType:(i)];
_bird.position = ccp(viewSize.width * 0.5f + 30 * i , viewSize.height * 0.9f - 15* i);
[self addChild:_bird];
[_arrayOfBirds addObject:_bird];
}
}
You forgot to initialize your array
here (assuming ARC)
-(id) init {
if(self=[super init]) {
_arrayOfBirds = [[NSMutableArray alloc] init];
// the rest
}
return self;
}
Im programming a game using sprite kit and i have written the accelerometer code and it works. But the problem is it doesn't stop when hitting the edge of the screen, can anyone help me out with this?
Here is the code for the player:
-(void)addShip
{
//initalizing spaceship node
ship = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
[ship setScale:0.5];
ship.zRotation = - M_PI / 2;
//Adding SpriteKit physicsBody for collision detection
ship.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ship.frame.size];
ship.physicsBody.mass = 0.02;
ship.physicsBody.categoryBitMask = shipCategory;
ship.physicsBody.dynamic = YES;
ship.physicsBody.affectedByGravity = NO;
ship.physicsBody.contactTestBitMask = DonutCategory | PizzaCategory | ChocolateCategory | SoftCategory | AppleCategory | GrapeCategory | OrangeCategory | BananaCategory;
ship.physicsBody.collisionBitMask = 0;
ship.physicsBody.usesPreciseCollisionDetection = YES;
ship.name = #"ship";
ship.position = CGPointMake(260,30);
[self addChild:ship];
motionManager = [[CMMotionManager alloc] init];
if ([motionManager isAccelerometerAvailable] == YES) {
[motionManager startAccelerometerUpdatesToQueue:[[NSOperationQueue alloc] init]
withHandler:^(CMAccelerometerData *data, NSError *error)
{
float destX, destY;
float currentX = ship.position.x;
float currentY = ship.position.y;
BOOL shouldMove = NO;
if(data.acceleration.x < -0.25) { // tilting the device to the right
destX = currentX + (data.acceleration.x * kPlayerSpeed);
destY = currentY;
shouldMove = YES;
} else if (data.acceleration.x > 0.25) { // tilting the device to the left
destX = currentX + (data.acceleration.x * kPlayerSpeed);
destY = currentY;
shouldMove = YES;
}
if(shouldMove) {
SKAction *action = [SKAction moveTo:CGPointMake(destX, destY) duration:1];
[ship runAction:action];
}
}];
}
}
You can add edges to your screen so the physics will stop the ship when it hits the edge (do it in init method):
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
//Add this to change some behaviour when ship will collide with the screen
//self.physicsBody.friction = 0.0f;
The other way is check in update method when ship is touching edge of the screen and change the ship position.
//Extended
I believe moveTo:duration: method mess up your phasic, to fix it just mate sure your destX and destX to go more that screen size width (height) - ship size width (height) and less that origin x and origin y.
You shouldn't use moveTo:duration: method, instead you should apply force to your ship.
Try this code, it's a little bit different that yours but this is much better way to move your ship (ignore the code above):
//Your ship setting
_ship.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_ship.frame.size];
_ship.physicsBody.mass = 0.02;
_ship.physicsBody.categoryBitMask = shipCategory;
_ship.physicsBody.dynamic = YES;
_ship.physicsBody.affectedByGravity = NO;
// Edge around the screen
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
//Apply movement (instead of moveTo:duration:)
//Get the accelerometer value
CMAccelerometerData* accelData = _motionManager.accelerometerData;
if (fabs(accelData.acceleration.x) > 0.2) {
// 35 is the value you can play with to make your ship movement feel more natural
[_ship.physicsBody applyForce:CGVectorMake(0.0, 35.0 * data.acceleration.x)];
}
I have a CCLayer which holds all my game objects and i have implemented scaling and scrolling. To make sure that you can't scroll out of bounds i am calculating a rect that represents the screen and use it to check if the rect is within bounds. The problem is that my calculations are wrong. The layer is scaled by a scaleFactor like so:
world->setScale(scaleFactor);
and then i calculate the scroll rect:
float scrollWidth = winSize.width * ( 1 / scaleFactor); // winSize is the size of the screen (1280x
float scrollHeight = winSize.height * ( 1 / scaleFactor);
if(scrollRect.l < 0) scrollRect.l = 0;
if(scrollRect.l + scrollWidth > levelWidth) scrollRect.l -= (scrollRect.l + scrollWidth - levelWidth);
scrollRect.r = scrollRect.l + scrollWidth;
world->setPosition(-scrollRect.l, -scrollRect.b);
(the scale factor value is between 1.0 and 0.5)
This sort of works but only when the layer is zoomed out to the max or zoomed in to the minimum but when scaleFactor isn't MAX/MIN it is wrong (there is some left space).
What am i doing wrong? I've also tried changing the anchor points (they are currently set to 0,0) but without any success.
You can do this whatever your scale factor...
here _tileMap is your world
//Code for getting difference b/w two position
CCTouch *fingerOne = (CCTouch*)touchArray->objectAtIndex(0);
CCPoint newTouchLocation = fingerOne->getLocationInView();
newTouchLocation = CCDirector::sharedDirector()->convertToGL(newTouchLocation);
newTouchLocation=_tileMap->convertToNodeSpace(newTouchLocation);
CCPoint oldTouchLocation = fingerOne->getPreviousLocationInView();
oldTouchLocation = CCDirector::sharedDirector()->convertToGL(oldTouchLocation);
oldTouchLocation = _tileMap->convertToNodeSpace(oldTouchLocation);
//get the difference in the finger touches when the player was dragging
CCPoint difference = ccpSub(newTouchLocation, oldTouchLocation);
CCPoint ASD=ccpAdd(_tileMap->getPosition(), ccpMult(difference, _tileMap->getScale()));
CCPoint bottomLeft =ASD;
// Bounding Box....
if (bottomLeft.x >0) {
bottomLeft.x = 0;
}
if (bottomLeft.y>0) {
bottomLeft.y = 0;
}
if (bottomLeft.x < -(mapWidth*_tileMap->getScale() - _screenSize.width)) {
bottomLeft.x = -(mapWidth*_tileMap->getScale()- _screenSize.width);
}
if (bottomLeft.y <-(mapHieght*_tileMap->getScale() - _screenSize.height)) {
bottomLeft.y = - (mapHieght*_tileMap->getScale() - _screenSize.height);
}
_tileMap->setPosition(bottomLeft);
I hope this may help you..
My game world space is a rectangle of 4 screen sizes (2 rows x 2 columns )
I am moving the camera based on the movement of a ball. The ball position is set according to the physics engine.
I notice that, sometimes the ball changes direction slightly as the camera is moving to follow the ball.
Initially I thought it was sort of an illusion based on the movement of the camera, but when I draw a path, the line is of crooked at times.
I used some of the code behind the CCFollow implementation code (not CCFollow itself, but the code behind it), for following the ball
(I had issues with CCfollow)
Below is my Tick() function:
————————————————–
- (void)tick:(ccTime) dt {
bool ballMov = false;
CGPoint pos;
CCSprite *ballData;
dt = 1.0 / 60.0f;
world->Step(dt,8, 3 );
for(b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *tempData = (CCSprite *)b->GetUserData();
if (tempData == ball){
ballData = tempData;
ballMov = true;
ballData.position = ccp(b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
}
}
}
//
// Move the layer so we can keep looking at the travelling ball
// Code is similar to CCFollow code with some adjustments
pos = ballPosition;
if(ballMov){
#define CLAMP(x,y,z) MIN(MAX(x,y),z)
pos = ccp(CLAMP(pos.x,leftBoundary,rightBoundary), CLAMP(pos.y,bottomBoundary,topBoundary));
#undef CLAMP
pos = ccpSub(pos, halfScreenSize );
pos.x = - pos.x ;
pos.y = - pos.y ;
//calculate delta move towards the new position
if(gunShot){
CGPoint moveVect;
CGPoint oldPos = [self position];
double dist = ccpDistance(pos, oldPos);
if (dist > 1){
moveVect = ccpMult(ccpSub(pos,oldPos),0.4); //0.05 is the smooth constant.
oldPos = ccpAdd(oldPos, moveVect);
pos = oldPos;
}
}
}
//move towards the ball
[self setPosition:(pos)];
lastPosition = pos;
ballPosition = CGPointMake([ball body]->GetPosition().x * PTM_RATIO, [ball body]->GetPosition().y * PTM_RATIO );
//if ball is out of gameworld space then reset
if (!CGRectContainsPoint ( outerBoundaryRect, ballPosition)){
ball.visible = false ;
[self recreateBody]; //Ball is out of Gameworld, recreate it at the default position
}
}
I have some small sprites (width/height) in a CCLayer and i want to detect which of them is touched. I use the following code.
- (BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
CGRect rect = sprite.boundingBox;
// i am doing this because of the small size of the sprites,
// so it would be more easy to detect if a sprite is taped and then move it.
if (rect.size.width > rect.size.height) {
rect.size.width *= 2.5;
rect.size.height *= 5;
rect.origin.y -= rect.size.height / 2;
} else {
rect.size.width *= 5;
rect.size.height *= 2.5;
rect.origin.x -= rect.size.width / 2;
}
CCSprite *s = nil;
for (CCSprite *sprite in [self children]) {
if (CGRectContainsPoint(rect, touchPoint)) {
s = sprite;
break;
}
}
if (s != nil) {
// do something here
}
return YES;
}
All works fine except when two sprites is very close (not overlap) to each other. Then because of the small distance between them, the wrong sprite is detected.
Any idea how could i correct it?
If you are trying to enlarge the rects keeping their center where it originally is, you should use:
rect.origin.y -= (rectFinalHeight - rectOriginalHeight) / 2;
and
rect.origin.y -= (rectFinalWidth - rectOriginalWidth) / 2;
which would be:
if (rect.size.width > rect.size.height) {
rect.size.width *= 2.5;
rect.size.height *= 5;
rect.origin.y -= rect.size.height * 4 / 2;
} else {
rect.size.width *= 5;
rect.size.height *= 2.5;
rect.origin.x -= rect.size.width * 4 / 2;
}
IMO, you should adjust the rect origin along both axis in both cases (and not only in one case along the x axis and in the other along the y axis), otherwise your rect will not be centered. But you understand better what you are trying to do, here...