I'm creating a game and I need the sprite to jump (move up and down basically) while its moving on a bezier path so it only moves vertically while it follows the path. If I do thids while it's moving along the bezier path:
[mySprite runAction:[CCJumpBy actionWithDuration:0.1 position:ccp(0,0) height:10 jumps:1]];
It jumps vertically but instantly it return to the position on the path. What I want is to jump relative to the path.
Anyone knows something about it?
Thanks
One thing I could imagine doing that is
put mySprite on the blank layer
do regular up and down action for mySprite item.
when you need, run action to the blank layer to Jump.
This would mix two different Action.
-(void) update:(ccTime) t
{
// parabolic jump (since v0.8.2)
ccTime frac = fmodf( t * jumps_, 1.0f );
ccTime y = height_ * 4 * frac * (1 - frac);
y += delta_.y * t;
ccTime x = delta_.x * t;
[target_ setPosition: ccp( ((CCNode*)target_).position.x + x, ((CCNode*)target_).position.y + y )];
}
Related
I am recently working with SFML libraries and I am trying to do a Space Shooter game from scratch. After some time working on it I get something that works fine but I am facing one issue and I do not know exactly how to proceed, so I hope your wisdom can lead me to a good solution. I will try to explain it the best I can:
Enemies following a path: currently in my game, I have enemies that can follow linear paths doing the following:
float vx = (float)m_wayPoints_v[m_wayPointsIndex_ui8].x - (float)m_pos_v.x;
float vy = (float)m_wayPoints_v[m_wayPointsIndex_ui8].y - (float)m_pos_v.y;
float len = sqrt(vx * vx + vy * vy);
//cout << len << endl;
if (len < 2.0f)
{
// Close enough, entity has arrived
//cout << "Has arrived" << endl;
m_wayPointsIndex_ui8++;
if (m_wayPointsIndex_ui8 >= m_wayPoints_v.size())
{
m_wayPointsIndex_ui8 = 0;
}
}
else
{
vx /= len;
vy /= len;
m_pos_v.x += vx * float(m_moveSpeed_ui16) * time;
m_pos_v.y += vy * float(m_moveSpeed_ui16) * time;
}
*m_wayPoints_v is a vector that basically holds the 2d points to be followed.
Related to this small piece of code, I have to say that is sometimes given me problems because getting closer to the next point becomes difficult as the higher the speed of the enemies is.
Is there any other way to be more accurate on path following independtly of the enemy speed? And also related to path following, if I would like to do an introduction of the enemies before each wave movement pattern starts (doing circles, spirals, ellipses or whatever before reaching the final point), for example:
For example, in the picture below:
The black line is the path I want a spaceship to follow before starting the IA pattern (move from left to right and from right to left) which is the red circle.
Is it done hardcoding all and each of the movements or is there any other better solution?
I hope I made myself clear on this...in case I did not, please let me know and I will give more details. Thank you very much in advance!
Way points
You need to add some additional information to the way points and the NPC's position in relationship to the way points.
The code snippet (pseudo code) shows how a set of way points can be created as a linked list. Each way point has a link and a distance to the next way point, and the total distance for this way point.
Then each step you just increase the NPC distance on the set of way points. If that distance is greater than the totalDistance at the next way point, follow the link to the next. You can use a while loop to search for the next way point so you will always be at the correct position no matter what your speed.
Once you are at the correct way point its just a matter of calculating the position the NPC is between the current and next way point.
Define a way point
class WayPoint {
public:
WayPoint(float, float);
float x, y, distanceToNext, totalDistance;
WayPoint next;
WayPoint addNext(WayPoint wp);
}
WayPoint::WayPoint(float px, float py) {
x = px; y = py;
distanceToNext = 0.0f;
totalDistance = 0.0f;
}
WayPoint WayPoint::addNext(WayPoint wp) {
next = wp;
distanceToNext = sqrt((next.x - x) * (next.x - x) + (next.y - y) * (next.y - y));
next.totalDistance = totalDistance + distanceToNext;
return wp;
}
Declaring and linking waypoints
WayPoint a(10.0f, 10.0f);
WayPoint b(100.0f, 400.0f);
WayPoint c(200.0f, 100.0f);
a.addNext(b);
b.addNext(c);
NPC follows way pointy path at any speed
WayPoint currentWayPoint = a;
NPC ship;
ship.distance += ship.speed * time;
while (ship.distance > currentWayPoint.next.totalDistance) {
currentWayPoint = currentWayPoint.next;
}
float unitDist = (ship.distance - currentWayPoint.totalDistance) / currentWayPoint.distanceToNext;
// NOTE to smooth the line following use the ease curve. See Bottom of answer
// float unitDist = sigBell((ship.distance - currentWayPoint.totalDistance) / currentWayPoint.distanceToNext);
ship.pos.x = (currentWayPoint.next.x - currentWayPoint.x) * unitDist + currentWayPoint.x;
ship.pos.y = (currentWayPoint.next.y - currentWayPoint.y) * unitDist + currentWayPoint.y;
Note you can link back to the start but be careful to check when the total distance goes back to zero in the while loop or you will end up in an infinite loop. When you pass zero recalc NPC distance as modulo of last way point totalDistance so you never travel more than one loop of way points to find the next.
eg in while loop if passing last way point
if (currentWayPoint.next.totalDistance == 0.0f) {
ship.distance = mod(ship.distance, currentWayPoint.totalDistance);
}
Smooth paths
Using the above method you can add additional information to the way points.
For example for each way point add a vector that is 90deg off the path to the next.
// 90 degh CW
offX = -(next.y - y) / distanceToNext; // Yes offX = - y
offY = (next.x - x) / distanceToNext; //
offDist = ?; // how far from the line you want to path to go
Then when you calculate the unitDist along the line between to way points you can use that unit dist to smoothly interpolate the offset
float unitDist = (ship.distance - currentWayPoint.totalDistance) / currentWayPoint.distanceToNext;
// very basic ease in and ease out or use sigBell curve
float unitOffset = unitDist < 0.5f ? (unitDist * 2.0f) * (unitDist * 2.0f) : sqrt((unitDist - 0.5f) * 2.0f);
float x = currentWayPoint.offX * currentWayPoint.offDist * unitOffset;
float y = currentWayPoint.offY * currentWayPoint.offDist * unitOffset;
ship.pos.x = (currentWayPoint.next.x - currentWayPoint.x) * unitDist + currentWayPoint.x + x;
ship.pos.y = (currentWayPoint.next.y - currentWayPoint.y) * unitDist + currentWayPoint.y + y;
Now if you add 3 way points with the first offDist a positive distance and the second a negative offDist you will get a path that does smooth curves as you show in the image.
Note that the actual speed of the NPC will change over each way point. The maths to get a constant speed using this method is too heavy to be worth the effort as for small offsets no one will notice. If your offset are too large then rethink your way point layout
Note The above method is a modification of a quadratic bezier curve where the control point is defined as an offset from center between end points
Sigmoid curve
You don't need to add the offsets as you can get some (limited) smoothing along the path by manipulating the unitDist value (See comment in first snippet)
Use the following to function convert unit values into a bell like curve sigBell and a standard ease out in curve. Use argument power to control the slopes of the curves.
float sigmoid(float unit, float power) { // power should be > 0. power 1 is straight line 2 is ease out ease in 0.5 is ease to center ease from center
float u = unit <= 0.0f ? 0.0f : (unit >= 1.0f ? 1.0f: unit); // clamp as float errors will show
float p = pow(u, power);
return p / (p + pow(1.0f - u, power));
}
float sigBell(float unit, float power) {
float u = unit < 0.5f ? unit * 2.0f : 1.0f - (unit - 0.5f) * 2.0f;
return sigmoid(u, power);
}
This doesn't answer your specific question. I'm just curious why you don't use the sfml type sf::Vector2 (or its typedefs 2i, 2u, 2f)? Seems like it would clean up some of your code maybe.
As far as the animation is concerned. You could consider loading the directions for the flight pattern you want into a stack or something. Then pop each position and move your ship to that position and render, repeat.
And if you want a sin-like flight path similar to your picture, you can find an equation similar to the flight path you like. Use desmos or something to make a cool graph that fits your need. Then iterate at w/e interval inputting each iteration into this equation, your results are your position at each iteration.
Well, I think I found one of the problems but I am not sure what the solution can be.
When using the piece of code I posted before, I found that there is a problem when reaching the destination point due to the speed value. Currently to move a space ship fluently, I need to set the speed to 200...which means that in these formulas:
m_pos_v.x += vx * float(m_moveSpeed_ui16) * time;
m_pos_v.y += vy * float(m_moveSpeed_ui16) * time;
The new position might exceed the "2.0f" tolerance so the space ship cannot find the destination point and it gets stuck because the minimum movement that can be done per frame (assuming 60fps) 200 * 1 / 60 = 3.33px. Is there any way this behavior can be avoided?
I am using the following code to make the camera follow my character as he falls down the screen:
[self runAction:[CCFollow actionWithTarget:charSprite worldBoundary:[levelLoader gameWorldSize]/*CGRectMake(0,0,320,3000)*/]];
What happens is this: the code runs as you would expect, and follows the character as he falls from the top of the screen. However: when he reaches the middle of the screen the camera stops following him.
I have tried both the code above as well as replacing the [levelLoader gameWorldSize] with the commented out CGRectMake(0,0,320,3000) - both yield the same results. As a last resort I tried to use some code I found on www.raywenderlich.com that has worked for me before (see below) (it worked on the x-axis in another game I wrote), but the same problem occurred.
-(void)setViewpointCenter:(CGPoint) position
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
CGRect worldRect = [levelLoader gameWorldSize];
int x = MAX(position.x, worldRect.origin.x + winSize.width / 2);
int y = MAX(position.y, worldRect.origin.y + winSize.height / 2);
x = MIN(x, (worldRect.origin.x + worldRect.size.width) - winSize.width / 2);
y = MIN(y, (worldRect.origin.y + worldRect.size.height) - winSize.height/2);
CGPoint actualPosition = ccp(x, y);
CGPoint centerOfView = ccp(winSize.width/2, winSize.height/2);
CGPoint viewPoint = ccpSub(centerOfView, actualPosition);
self.position = viewPoint;
}
My world is created in Portrait mode using LevelHelper.
Any help would be greatly appreciated!
CCFollow follows the node within the world boundary, minus half the screen width/height. If your sprite starts falling at y = 320 then CCFollow will follow it until y = 160.
You can either position the sprite to your world boundary's top (y = 3000) or make the world boundary negative (y = -3000). Either way, when the sprite reaches the world boundary, scrolling will stop half a screen width/height before the world boundary.
As I am newbie to cocoa2d I am struggling alot to rotate the physics or dynamic body along an arc path.
The way I tried is as follows:
#define COS_ANIMATOR(position, timeCount, speed, waveMagnitude) ((cosf(timeCount * speed) * waveMagnitude) + position)
#define SIN_ANIMATOR(position, timeCount, speed, waveMagnitude) ((sinf(timeCount * speed) * waveMagnitude) + position)
CCSpriteBatchNode *pipe_parent = [CCSpriteBatchNode batchNodeWithFile:#"pipe.png" capacity:100];
CCTexture2D *pipeSpriteTexture_ = [pipe_parent texture];
PhysicsSprite *pipeSprite = [PhysicsSprite spriteWithTexture:pipeSpriteTexture_ rect:CGRectMake(0 ,0 ,55,122)];
//pipe = [CCSprite spriteWithFile:#"pipe.png"
// rect:CGRectMake(0, 0, 55, 122)];
[self addChild:pipeSprite];
// pipe.position = ccp(s.width/2 , 420.0);
b2BodyDef myBodyDef;
myBodyDef.type = b2_staticBody; //this will be a dynamic body
myBodyDef.position.Set(((s.width/2) - 90)/PTM_RATIO, 420.0/PTM_RATIO); //set the starting position
myBodyDef.angle = 0; //set the starting angle
b2Body* staticBody = world->CreateBody(&myBodyDef);
b2PolygonShape boxShape;
boxShape.SetAsBox(1,1);
b2FixtureDef boxFixtureDef;
boxFixtureDef.shape = &boxShape;
boxFixtureDef.density = 1;
boxFixtureDef.userData = pipeSprite;
boxFixtureDef.filter.groupIndex = -1;
staticBody->CreateFixture(&boxFixtureDef);
[pipeSprite setPhysicsBody:staticBody];
-(void) draw
{
//
// IMPORTANT:
// This is only for debug purposes
// It is recommend to disable it
//
[super draw];
const CGPoint newSpritePosition = ccp(COS_ANIMATOR(150, mTimeCounter, 0.05,50), SIN_ANIMATOR(400, mTimeCounter, -0.05, 50));
pipeSprite.position = newSpritePosition;
ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );
kmGLPushMatrix();
world->DrawDebugData();
kmGLPopMatrix();
}
on following this approach my sprite rotating in circular motion instead of rotating in an arc path.
Please give your ideas or suggestions.
Thanks all
I'm not entirely sure what it is you are looking to accomplish when you talk about rotating in an arc path. I only see you setting a position, not a rotation, so are you just wanting to set a position, or a rotation, or both? Your position code looks like you are trying to achieve a circular (or elliptical) path because you are using the sine and cosine in the x,y position.
If you're looking to move a sprite along a sine curve, I did that today and it took a bit of trial and error. I had some variables for the amplitude and period, and from there I traced out a nice sine curve movement in the sprite's update: method.
CGPoint initialPosition; // set this to the sprite's initial position
float amplitude; // in points
float period; // in points
float y, x = initialPosition.x;
-(void) update:(ccTime)dt
{
x += dt * 100; // speed of movement across the screen. Picked by trial and error.
y = initalPosition.y + amplitude * sinf((x - initialPosition.x)/period);
sprite.position = ccp(x,y);
sprite.rotation = cosf((x - initialPosition.x)/period); // optional if you want to rotate along the path as well
}
Don't know if this is anything you are looking for but it might give you some ideas.
Hi I have finally made a working joystick in cocos2d. I am able to rotate a sprite to the exact angle that the joystick thumb, or cap, is 'pointing'. However, I am unable to move the sprite in that same direction. Is there an easy way to move the sprite with the way I have the rotating code set up? Also is there a way to keep it moving if your thumb is still pressed, but not moving the joystick?. PS this code is all within the TouchesMoved method. PPS. the cap is the thumb, the pad is the joystick background, and the Sprite2 is the sprite that I want to move. (95, 95) is the center of the pad sprite.
if(capSprite.position.x>=padSprite.position.x){
id a3 = [CCFlipX actionWithFlipX:NO];
[sprite2 runAction:a3];
}
if(capSprite.position.x<=padSprite.position.x){
id a4 = [CCFlipX actionWithFlipX:YES];
[sprite2 runAction:a4];
}
CGPoint pos1 = ccp(95, 95);
CGPoint pos2 = ccp(capSprite.position.x, capSprite.position.y);
int offX = pos2.x-pos1.x;
int offY = pos2.y-pos1.y;
float angleRadians = atanf((float)offY/(float)offX);
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
float theAngle = -1 * angleDegrees;
sprite2.rotation = theAngle;
I'm not familiar with cocos2d but I had a quick look at the documentation and this sample might be of use to you:
if keys[key.UP]:
self.target.acceleration = (200 * rotation_x, 200 * rotation_y)
I had written a long explanation answering your second question but I believe this "self.target.acceleration" solves that too. You can read more at the cocos2d API documentation.
What I generally do is get the angle, convert it to a CGPoint with ccpForAngle(float) and then multiply the CGPoint by a value:
float angle = whatever;
CGPoint anglePoint = ccpForAngle(angle);
// You will need to play with the mult value
angle = ccpMult(angle, 2.5);
// This also works with box2D or probably Chipmunk.
sprite.position = angle;
I'm using Dark GDK and C++ to create a simple 2d game. I'm rotating an object but it rotates from the top left corner of the sprite.
I have the following variables available:
PlayerX
PlayerY
PlayerWidth
PlayerHeight
RotateAngle (360 > x > 0)
Is there an algorithm that will modify the pivot point of the sprite, preferable to the center?
Here is a small code sample:
void Player::Move( void )
{
if ( checkLeft() )
{
PlayerX -= PlayerSpeed;
if ( PlayerX < 0 )
PlayerX = 0;
}
if ( checkRight() )
{
PlayerX += PlayerSpeed ;
if ( PlayerX > 800 - PlayerWidth )
PlayerX = 800 - PlayerWidth;
}
if ( checkUp())
{
PlayerY -= PlayerSpeed;
if ( PlayerY < 0 )
PlayerY = 0;
}
if ( checkDown())
{
PlayerY += PlayerSpeed;
if ( PlayerY > 600 - PlayerHeight)
PlayerY = 600 - PlayerHeight;
}
RotateAngle += 5;
if(RotateAngle > 360)
RotateAngle -=360;
dbRotateSprite(Handle,RotateAngle);
dbSprite ( 1 , PlayerX , PlayerY , Handle );
}
Edit
I'm considering opening up some reputation for this question, I have yet to be provided with an answer that works for me. If someone can provide an actual code sample that does the job, I'd be very happy.
The problem with Blindy's answer is that no matter how much I simply translate it back or forth, the spirte still rotates around the top left hand corner and moving it somewhere rotating around the top left corner, then moving it back to the same position accomplishes nothing. Here is what I believe to be going on:
alt text http://img248.imageshack.us/img248/6717/36512474.png
Just so there is no confusion I have created a an image of what is going on. The left shows what is actually happening and the right shows what I need to happen.
alt text http://img101.imageshack.us/img101/1593/36679446.png
You'd need to do something like:
translate by (-playerx-playerwidth/2, -playery-playerheight/2)
rotate by rotateangle
translate by (playerx+playerwidth/2, playery+playerheight/2)
The idea is to center your sprite on the origin then rotate around the origin (glRotate) and after you get the rotated sprite you translate it back in its place.
NB: If your sprite is initially "centered" around the origin, but with a corner not the actual center of the sprite, you first translate the object to center the sprite's center with the origin. So like if your sprite had the top-left corner in the origin, you'd translate by (-playerwidth/2, -playerheight/2), then rotate then translate by (playerx,playery).
The answers so far are correct in telling you how it should be done but I fear that the Dark GDK API seems to be too primitive to be able to do it that simple way.
Unfortunately dbRotateSprite rotates the sprite about the top left regardless of the sprite's transform which is why you're having no luck with the other suggestions. To simulate rotation about the centre you must manually correct the position of the sprite i.e. you simply have to rotate the sprite and then move it as a two-step process.
I'm not familiar with the API and I don't know if y is measured up or down and which way the angle is measured so I'm going to make some assumptions. If y is measured down like many other 2D graphics systems, and the angle is measured from the x-axis increasing as it goes from the positive x-axis to the positive y-axis, then I believe the correct psuedo-code would look like
// PlayerX and PlayerY denote the sprite centre
// RotateAngle is an absolute rotation i.e. not a relative, incremental rotation
RotateAngle += 5;
RotateAngle %= 360;
RadiansRotate = (RotateAngle * PI) / 180;
dbRotateSprite( Handle, RotateAngle );
HalfSpriteWidth = dbSpriteWidth( Handle ) / 2;
HalfSpriteHeight = dbSpriteHeight( Handle ) / 2;
SpriteX = PlayerX
- HalfSpriteWidth * cos(RadiansRotate)
+ HalfSpriteHeight * sin(RadiansRotate);
SpriteY = PlayerY
- HalfSpriteHeight * cos(RadiansRotate)
- HalfSpriteWidth * sin(RadiansRotate);
// Position the top left of the sprite at ( SpriteX, SpriteY )
dbSprite ( 1 , SpriteX , SpriteY , Handle );
When you rotate the object, you are applying a transformation to the points that compose the object. In this case, the four corners each rotate on their own, and the end result is the quad formed in their new locations. As everyone else has mentioned, the critical part of this is knowing where the origin about which the points rotate.
Imagine if instead of a sprite, you only had one point. If its origin was at the same position as that point, rotating it would have no effect (the position of the point would not move). However, if the origin was anywhere else, the point would rotate in a circle, with the origin as the center of that circle. And how would you get that origin to be somewhere other than at the same position as the point? Move the point within the coordinate system. In other words, translate it.
The order of the various transformations (rotate, translate, scale, etc.) is critical to this. If you rotated the point 180 degrees and then translated it to the right, it would end up to the right of where it started, but if you moved it to the right and then rotated it 180 degrees, it would end up on the left.
I'd recommend reading up on 2D transformations. Understanding how matrices play in all this would also be useful, but not strictly necessary to get the effect you're looking for.
Looking at your image, what you're doing is you're rotating the square in place, you're not translating it at all. This is what you're doing:
^ ^
| |
| |
| ====> |
| |
+--+-----> x---------->
| | / \
+--+ \ /
x
As you can see, your square is with its top-left corner in the origin and all you're doing is rotating it around the origin.
This is what I'm saying you should do:
^ ^ ^
| | |
| | ===> | ==> translate to
| ====> | x ==> where you want
| +-+ /|\ ==> to draw it
+--+-----> |+|---------> +-------->
| | +-+ \ /
+--+ x
You can only rotate around the center, so center the point you want to rotate your primitives around then place them where you want.
OMG ive been tryna do this for a while now to, n i found this page, just copy this.
this is based on one of the previous posts.
void dbRotateSpriteCenter(int iID, int iX, int iY, int fRotate, int iImage)
{
int x = iX
- dbSpriteWidth(iID) / 2 * cos(fRotate * 3.1415926536 / 180)
+ dbSpriteHeight(iID) / 2 * sin(fRotate * 3.1415926536 / 180);
int y = iY
- dbSpriteHeight(iID) / 2 * cos(fRotate * 3.1415926536 / 180)
- dbSpriteWidth(iID) / 2 * sin(fRotate * 3.1415926536 / 180);
dbRotateSprite(iID, fRotate);
dbSprite(iID, x, y, iImage);
}
you could use dbOffsetSprite();
when sprites are created, their insertion point is upper-left corner by default (and dbRotateSprite(); rotates them around the insertion point), you can use this function to change the insertion point. its format is dbOffsetSprite(sprite number, amount to offset X, amount to offset Y);
so you could say
dbOffsetSprite(NUMBER, dbSpriteWidth(NUMBER) / 2, dbSpriteHeight(NUMBER) / 2);
where NUMBER is the sprite ID number.
The sprite's insertion point is now in the center of its image.
Of course, this might open a whole new can of worms, as the insertion point is now in the center of the sprite (or where ever you set it), meaning calling dbSpriteX(NUMBER); will give you the center of the sprite instead of the edge.