I searched and couldn't find a solution to this (maybe I'm using the wrong terms?), and I feel a bit silly because I believe I'm overlooking something simple. Here's what I'd like to do:
I have an object in my game that needs to travel a specific x distance in a specified number of frames. But, I'd like to have it ease into the target point instead of moving the same distance/velocity every frame.
So rather than just dividing the number of frames by the distance, and applying that velocity each frame, I'd I'd like to slowly ease itself into the target coordinate in a specified number of frames.
So for example (I'm terrible at explaining, perhaps an example could help)... say I have a spaceship, and he needs to travel 32 pixels to the right. I'd like to input a value that specifies he'll travel 32 pixels in... say, 4 frames. In a linear system, he'd travel 8 pixels each frame, but I want him to ease into it, so maybe on frame 1 (and I'm using completely random values) he'd move 16 pixels, frame 2 he'd move 10, frame 3 he'd move 4, and frame 4 he'd move 2, and he'll end up traveling the 32 pixels distance in those 4 frames, slowly easing into the target point.
The first thing that came to mind was using exponent/logarithms somehow, but I'd like some suggestions. Any help would be greatly appreciated, thanks! :D
The general solution is the following:
You have a value (distanceTravelled) which has a range from 0.0 to 1.0.
You have a function (fancyCurve) which takes in a float from 0.0 to 1.0 and remaps it from 0.0 to 1.0, except in a curve.
Every frame, you increase distanceTravelled by a linear amount. But you get the actual value by calling fancyCurve(distanceTravelled).
Here's a pseudo-code example
float linearFunction(float t) { return t; }
float speedUpFunction(float t) { return t*t; }
float slowDownFunction(float t)
{
//do your own research. Theres plenty of curves from http://easings.net/
}
float easingCurve(float t) {
//choose one.
//return linearFunction(t);
//return speedUpFunction(t);
return slowDownFunction(t);
}
int main() {
//setting up a spaceship with starting x coordinate
spaceshipX = 2;
spaceshipTargetX = 34;
animationFrames = 8;
//Below is the actual algorithm
distanceTravelled = 0;
startValue = spaceshipX;
animRange = spaceshipTargetX - startValue; // 32
distancePerFrame = 1.0f / animationFrames; // 0.125, since 8 frames
while (distanceTravelled < 1.0f) {
WaitForEndOfFrame();
distanceTravelled += distancePerFrame;
spaceshipX = startValue + (easingCurve(distanceTravelled) * animRange);
}
}
Related
Well Hello,
I'm making a 2d platformer for iOS using spritekit. I have moving platforms to allow my characters to move with the platform.
I can't just use skactions to move my platforms because the character will not move with the platform.
question:
How would I add an ease in and out function in order to have the platforms??? simulate: SKactionTimeMode.easeInEaseOut
Current Solution:
I don't have the code in front of me, but for a left/right moving platform this is pretty much what I'm doing. This would be running within the platforms update() method.
If platform.position.x < xPositionIWantNodeToStopGoingLeft {
velAmount = -velAmount
}
else if platform.position.x > xPositionIWantNodeToStopGoingRight {
velAmount = -velAmount
}
platform.physicsBody?.velocity = SKVector(dx: velAmount, dy: velAmount
platform.position.y = staticYPosition
Just to clarify, this works great. If there is a better way to do this I'm all ears. But this creates a jagged stop and turn kind of feel. I want that ease in and out feel so that the platform feels more natural.
Thanks for any help!!!
Ease in out function
If we consider the time for the platform to move from one side to the other as one unit ( it might be 10 seconds, or 17 frames, it does not matter, we work in units for now).
We do the same with the distance. The platform must move one unit distance in one unit of time.
For this answer time is t and the position is a function of time written as f(t) is the platform position at time t.
For simple linear movement then the function is simply f(t)=t. So at time t=0 the distance moved is 0, at time 0.5 (half way) the distance is 0.5 (half way), and so on.
So lets put that into something a little more practical.
Please excuse my swift I have never used it befor (I am sure you can correct any syntax I get wrong).
// First normalise the distance and time (make them one unit long)
// get the distance
let distance = Double(xPositionStopGoingLeft - xPositionStopGoingRight);
// use that and the velocity to get the time to travel
let timeToTravel = distance / Double(velAmountX);
// first we have a frame ticker
gameTick += 1; // that ticks for every frame
// We can assume that the platform is always moving back and forth
// Now is the unit time where at now = 2 the platform has move there and back
// at 3 it has move across again and at 4 back again.
let now = Double(gameTick) / timeToTravel; // normalize time.
// get the remainder of 2 as from 0-1 is moving forward and 1-2 is back
let phase = now % 2.0;
// We also need the unit time for the function f(t)=t
let t = abs(phase - 1);
if phase >= 1 { t = 1 - t } // reverse for return
// implement the function f(t) = t where f(t) is dist
let dist = t
// and convert back to pixel distance
platform.position.x = Int(dist * distance + Double(xPositionStopGoingLeft));
So that is the linear platform. To make the movement change all we need to do is change the function f(t)=?, in the above its the line let dist = t
For a ease in out there is a handy function that is used in most ease applications f(t) = t * t / ((t * t) + (1 - t) * ( 1 - t))
There are some t*t which are powers, t to the power of 2 or t^2 . In swift its pow(t,2) so rewriting the above as code
let dist = pow(t,2) / (pow(t,2) + pow((1-t),2);
This gives a nice ease at the start and end As the distance and time traveled is constant the speed at the middle point t = 0.5 must be greater to catch up with the slow start and end. (Side note, Get the derivative of the above function lets you workout the speed at every point in time f'(t) = speed(t) = 2(-(t-1)t)^(2-1) /(t^2+(1-t)^2)^2)
This function is so nice, the speed at time 0.5 is 2, the same as the power (for the linear journey it would be 1). A handy property of the function is that the speed at the mid way point is always the same as the power. If you want it to move really fast at the midpoint say 4 times as fast then you use the power of 4
let dist = pow(t,4) / (pow(t,4) + pow((1-t),4);
If you want it only to speed up a little say 1.2 times the speed at the center then the power is 1.2
let dist = pow(t,1.2) / (pow(t,1.2) + pow((1-t),1.2);
So now we can introduce another term, maxSpeed which is the normalised maxSpeed (Side note more precisely it is the speed at t=0.5 as it can be the slower than 1, but for our need max speed will do)
let maxSpeed = Double(velAmountX + 3) / Double(velAmountX); // 3 pixels per frame faster
and the function f(t) = t^m / (t^m + (1-t)^m) where m is maxSpeed.
and as code
let dist = pow(t,maxSpeed ) / (pow(t,maxSpeed ) + pow((1-t),maxSpeed);
So put that all together
// the next 3 lines can be constats
let distance = Double(xPositionStopGoingLeft - xPositionStopGoingRight);
let timeToTravel = distance / Double(velAmountX);
let maxSpeed = Double(velAmountX + 3) / Double(velAmountX);
gameTick += 1; // that ticks for every frame
let now = Double(gameTick) / timeToTravel; // normalize time.
let phase = now % 2.0;
let t = abs(phase - 1);
if phase >= 1 { t = 1 - t } // reverse for return
// the next line is the ease function
let dist = pow(t, maxSpeed) / (pow(t, maxSpeed) + pow((1-t) ,maxSpeed);
// position the platform
platform.position.x = Int(dist * distance + Double(xPositionStopGoingLeft));
Now you can at any tick calculate the position of the platform. If you want to slow the whole game down and step frames at half ticks it still will work. if you speed the game up gameTick += 2 it still works.
Also the max speed can be lower than the linear speed. If you want the platform to be half the normal speed at the center t=0.5 the set maxSpeed = 0.5 and at the halfway point the speed will be half. To keep everything working the ease at the start and end will be quicker a rush in and rush out. (and works for reverse as well)
To help maybe a visual representation
Image shows the movement of the platform back and forth over time. The distance is about 60 pixels and the time can be 1 minute. So at 1 min it will be one the right 2min on the left, and so on.
Then we normalise the movement and time by looking only at one section of movement.
The graph represents the movement from left to right side, the distance is 1, and the time is 1. It has just been scaled to fit the unit box (1 by 1 box).
The red line represent the linear movement f(t)=t (constant speed). At any point of time you move across hit the line move down and you can find the distance traveled.
The green line represents the ease function f(t)=t*t/(t*t+(1-t)*(1-t)) and it works the same. At any point of time scan across to find the green line and move down to get the distance. the function f(t) does that for you.
With the maxSpeed the steepness of the line at dist 0.5 is changed, with steeper slope representing faster travel.
For physic, play with friction and linear damping of the body. You can even use an SKAction run block to reduce or add friction.
you could do something like:
physicsBody.friction = (10 - physicsBody.velocity.dx) > 0 ? (10 - physicsBody.velocity.dx) / 10 : 0
Basically it gives friction when velocity.dx is < 10, you may want to tweak the 10 to the number of your liking
Recently I had the idea to make a pendulum out of points using Processing, and with a little learning I solved it easily:
int contador = 0;
int curvatura = 2;
float pendulo;
void setup(){
size(300,300);
}
void draw(){
background(100);
contador = (contador + 1) % 360; //"CONTADOR" GOES FROM 0 TO 359
pendulo = sin(radians(contador))*curvatura; //"PENDULO" EQUALS THE SIN OF CONTADOR, SO IT GOES FROM 1 TO -1 REPEATEDLY, THEN IS MULTIPLIED TO EMPHASIZE OR REDUCE THE CURVATURE OF THE LINE.
tallo(width/2,height/3);
println(pendulo);
}
void tallo (int x, int y){ //THE FUNTION TO DRAW THE DOTTED LINE
pushMatrix();
translate(x,y);
float _y = 0.0;
for(int i = 0; i < 25; i++){ //CREATES THE POINTS SEQUENCE.
ellipse(0,0,5,5);
_y+=5;
rotate(radians(pendulo)); //ROTATE THEM ON EACH ITERATION, THIS MAKES THE SPIRAL.
}
popMatrix();
}
So, in a brief, what I did was a function that changed every point position with the rotate fuction, and then I just had to draw the ellipses in the origin coordinates as that is the real thing that changes position and creates the pendulum ilussion.
[capture example, I just need 2 more points if you are so gentile :)]
[capture example]
[capture example]
Everything was OK that far. The problem appeared when I tried to replace the ellipses for a path made of vertices. The problem is obvious: the path is never (visually) made because all vertices would be 0,0 as they move along with the zero coordinates.
So, in order to make the path possible, I need the absolute values for each vertex; and there's the question: How do I get them?
What I know I have to do is to remove the transform functions, create the variables for the X and Y position and update them inside the for, but then what? That's why I cleared this is a maths issue, which operation I have to add in the X and Y variables in order to make the path and its curvature possible?
void tallo (int x, int y){
pushMatrix();
translate(x,y);
//NOW WE START WITH THE CHANGES. LET'S DECLARE THE VARIABLES FOR THE COORDINATES
float _x = 0.0;
float _y = 0.0;
beginShape();
for(int i = 0; i < 25; i++){ //CREATES THE DOTS.
vertex(_x,_y); //CHANGING TO VERTICES AND CALLING THE NEW VARIABLES, OK.
//rotate(radians(pendulo)); <--- HERE IS MY PROBLEM. HOW DO I CONVERT THIS INTO X AND Y COORDINATES?
//_x = _x + ????;
_y = _y + 5 /* + ???? */;
}
endShape();
popMatrix();
}
We need to have in mind that pendulo's x and y values changes in each iteration of the for, it doesn't has to add the same quantity each time. The addition must be progressive. Otherwise, we would see a straight line rotating instead of a curve accentuating its curvature (if you increase curvatura's value to a number greater than 20, you will notice the spiral)
So, rotating the coordinates was a great solution to it, now it's kind of a muddle to think the mathematical solution to the x and y coordinates for the spiral, my secondary's knowledges aren't enough. I know I have to create another variable inside the for in order to do this progression, but what operation should it have?
I would be really glad to know, maths
You could use simple trigonometry. You know the angle and the hypotenuse, so you use cos to get the relative x position, and sin to the y. The position would be relative to the central point.
But before i explain in detail and draw some explanations, let me propose another solution: PVectors
void setup() {
size(400,400);
frameRate(60);
center = new PVector(width/2, height/3); //defined here because width and height only are set after size()
}
void draw() {
background(255);
fill(0);
stroke(0);
angle = arc_magn*sin( (float) frameCount/60 );
draw_pendulum( center );
}
PVector center;
float angle = 0;
float arc_magn = HALF_PI;
float wire_length = 150;
float rotation_angle = PI/20 /60 ; //we divide it by 60 so the first part is the rotation in one second
void draw_pendulum(PVector origin){
PVector temp_vect = PVector.fromAngle( angle + HALF_PI);
temp_vect.setMag(wire_length);
PVector final_pos = new PVector(origin.x+temp_vect.x, origin.y+temp_vect.y );
ellipse( final_pos.x, final_pos.y, 40, 40);
line(origin.x, origin.y, final_pos.x, final_pos.y);
}
You use PVector class static method fromAngle( float angle ) that returns a unity vector of the given angle, then use .setMag() to define it's length.
Those PVector methods will take care of the trigonometry for you.
If you still want to know the math behind it, i can make another example.
I'm developing map-based game using cocos2d v3.
I have a map with size of 2^19 points. On that map I have object that should move over time in short distance. About 60-70 points.
CGPoint offset = [_trajectoryPath offsetForNextPosition];
CGFloat x = self.position.x + offset.x;
CGFloat y = self.position.y + offset.y;
self.position = CGPointMake(x, y);
At such map size map position can be something like {300000, 40000} points.
When I try to add small step, lets say about {0.002f, 0.004f}, to animate object position I end up with still the same {300000, 40000} points...
I understand that it happens because of precision of float. Values normalised by map size, to be between 0 and 1.0, don't work either.
Is it possible somehow to increase precision of float type on iOS? Or may be someone cam give a hint about possible workaround for this problem?
Thanks.
mightee.cactus, I remember we had a similar issue while adding very small numbers to very large ones with float in c.
The solution was follows: we changed types to double to preserve accuracy; in your case you can make all the arithmetic operations with doubles and translate them into CGFloat just before use in CGPointMake.
I am doing some computer vision based hand gesture recognising stuff. Here, I want to detect a circle (a circular motion) made by my hand. My initial stages are working fine and I am able to get a blob whose centroid from each frame I am plotting. This is essentially my data set. A collection of 2D co-ordinate points. Now I want to detect a circular type motion and say generate a call to a function which says "Circle Detected". The circle detector will give a YES / NO boolean output.
Here is a sample of the data set I am generating in 40 frames
The x, y values are just plotted to a bitmap image using MATLAB.
My initial hand movement was slow and later I picked up speed to complete the circle within stipulated time (40 frames). There is no hard and fast rule about the number of frames thing but for now I am using a 40 frame sliding window for circle detection (0-39) then (1-40) then (2-41) etc.
I am also calculating the arc-tangent between successive points using:
angle = atan2(prev_y - y, prev_x - x) * 180 / pi;
Now what approach should I take for detecting a circle (This sample image should result in a YES). The angle as I am noticing is not steadily increasing from 0 to 360. It does increase but with jumps here and there.
If you are only interested in full or nearly full circles:
I think that the standard parameter estimation approach: Hough/RANSAC won't work very well in this case.
Since you have frames order and therefore distances between consecutive blob centers, you can create a nearly uniform sub sample of the data (let say, pick 20 points spaced ~evenly), calculate the center and measure the distance of all points from that center.
If it is nearly a circle all points will have similar distance from the center.
If you want to do something slightly more robust, you can:
Compute center (mean) of all points.
Perform gradient descent to update the center: should be fairly easy an you won't have local minima. The error term I would probably use is max(D) - min(D) where D is the vector of distances between the blob centers and estimated circle center (but you can use robust statistics instead of max & min)
Evaluate the circle
I would use a Least Square estimation. Numerically you can use the Nelder-Mead method. You get the circle that best approximate your points and on the basis of the residual error value you decide whether to consider the circle valid or not.
Being points the array of the points, xc, yc the coordinates of the center and r the radius, this could be an example of error to minimize:
class Circle
{
private PointF[] _points;
public Circle(PointF[] points)
{
_points = points;
}
public double MinimizeFunction(double xc, double yc, double r)
{
double d, d2, dx, dy, sum;
sum = 0;
foreach(PointF p in _points)
{
dx = p.X - xc;
dy = p.Y - yc;
d2 = dx * dx + dy * dy;
// sum += d2 - r * r;
d = Math.Sqrt(d2) - r;
sum += d * d;
}
return sum;
}
public double ResidualError(double xc, double yc, double r)
{
return Math.Sqrt(MinimizeFunctional(xc, yc, r)) / (_points.Length - 3);
}
}
There is a slight difference between the commented functional and the uncommented, but for practical reason this difference is meaningless. Instead, from a theoretical point of view the difference is important.
Since you need to supply a initial values set (xc, yc, r), you can calculate the circle given three points, choosing three points far from each other.
If you need more details on "circle given three points" or Nelder-Mead you can google or ask me here.
I'm studied the pARK example project (http://developer.apple.com/library/IOS/#samplecode/pARk/Introduction/Intro.html#//apple_ref/doc/uid/DTS40011083) so I can apply some of its fundamentals in an app i'm working on. I understand nearly everything, except:
The way it has to calculate if a point of interest must appear or not. It gets the attitude, multiply it with the projection matrix (to get the rotation in GL coords?), then multiply that matrix with the coordinates of the point of interest and, at last, look at the last coordinate of that vector to find out if the point of interest must be shown. Which are the mathematic fundamentals of this?
Thanks a lot!!
I assume you are referring to the following method:
- (void)drawRect:(CGRect)rect
{
if (placesOfInterestCoordinates == nil) {
return;
}
mat4f_t projectionCameraTransform;
multiplyMatrixAndMatrix(projectionCameraTransform, projectionTransform, cameraTransform);
int i = 0;
for (PlaceOfInterest *poi in [placesOfInterest objectEnumerator]) {
vec4f_t v;
multiplyMatrixAndVector(v, projectionCameraTransform, placesOfInterestCoordinates[i]);
float x = (v[0] / v[3] + 1.0f) * 0.5f;
float y = (v[1] / v[3] + 1.0f) * 0.5f;
if (v[2] < 0.0f) {
poi.view.center = CGPointMake(x*self.bounds.size.width, self.bounds.size.height-y*self.bounds.size.height);
poi.view.hidden = NO;
} else {
poi.view.hidden = YES;
}
i++;
}
}
This is performing an OpenGL like vertex transformation on the places of interest to check if they are in a viewable frustum. The frustum is created in the following line:
createProjectionMatrix(projectionTransform, 60.0f*DEGREES_TO_RADIANS, self.bounds.size.width*1.0f / self.bounds.size.height, 0.25f, 1000.0f);
This sets up a frustum with a 60 degree field of view, a near clipping plane of 0.25 and a far clipping plane of 1000. Any point of interest that is further away than 1000 units will then not be visible.
So, to step through the code, first the projection matrix that sets up the frustum, and the camera view matrix, which simply rotates the object so it is the right way up relative to the camera, are multiplied together. Then, for each place of interest, its location is multiplied by the viewProjection matrix. This will project the location of the place of interest into the view frustum, applying rotation and perspective.
The next two lines then convert the transformed location of the place into whats known as normalized device coordinates. The 4 component vector needs to be collapsed to 3 dimensional space, this is achieved by projecting it onto the plane w == 1, by dividing the vector by its w component, v[3]. It is then possible to determine if the point lies within the projection frustum by checking if its coordinates lie in the cube with side length 2 with origin [0, 0, 0]. In this case, the x and y coordinates are being biased from the range [-1 1] to [0 1] to match up with the UIKit coordinate system, by adding 1 and dividing by 2.
Next, the v[2] component, z, is checked to see if it is greater than 0. This is actually incorrect as it has not been biased, it should be checked to see if it is greater than -1. This will detect if the place of interest is in the first half of the projection frustum, if it is then the object is deemed visible and displayed.
If you are unfamiliar with vertex projection and coordinate systems, this is a huge topic with a fairly steep learning curve. There is however a lot of material online covering it, here are a couple of links to get you started:
http://www.falloutsoftware.com/tutorials/gl/gl0.htm
http://www.opengl.org/wiki/Vertex_Transformation
Good luck//