I'm making a little game. It's a 2D game, and the main feature is to change the direction of gravity. I managed to change the direction, so now the player "falls" to the direction fo the gravity. But now i want the player to "stabilize" in eg. 2 seconds. I implemented basic rotation but that instantly changes the angle of the player, and i want it to "slice" the steps to smaller pieces so the rotation will be "smoother". eg. i want to change from 180° to 0° in small steps calculated from the delta time, in relation with a number I input which will be the "duration"
Im not quite familiar with radians and thats why i cant use it.
The gravity direction can be set using world.gravitydir variable, it can be 1,2,3,4. 1 is normal gravity "down" 2,4 is "left" and "right", and 3 is "up"
I also have some "dev commands" to change the gravity direction manually using arrow keys
This is my attempt on rotating the player from upside down to normal smoothly.
function rotatePlayer(dt)
deg1 = player.rot -- player's current rotation
step = 0
deg2 = math.rad(0) -- desired rotation
step = (math.deg(deg1) - math.deg(deg2))*dt
for i = deg1, deg2 do
player.rot = player.rot - math.rad(step)
step = step - dt
end
end
playerrotation function is in gravity.lua , and the dev controller, and the player drawing function is in player.lua
Source: http://www.mediafire.com/download/3xto995yz638n0n/notitle.love
I've noticed a couple issues with your code.
deg2 = math.rad(0) -- desired rotation
0 radians is equivalent to 0 degrees, so it would seem deg2 is always zero, thus
for i = deg1, deg2 do
would only run when deg1 is equal to or less than zero, which likely means it isn't running as you intended.
Secondly, any variables that don't need to leave their scope should be localized. This is a Lua best practice. Your function could be rewritten with locals, for example:
function rotatePlayer(dt)
local deg1 = player.rot -- player's current rotation
local step = 0 -- ! Any reference to a "step" variable within the scope of this function will always refer to this particular variable.
local deg2 = 0 -- desired rotation
step = (math.deg(deg1) - math.deg(deg2))*dt
for i = deg1, deg2 do
player.rot = player.rot - math.rad(step)
step = step - dt
end
end
The following line is equivalent to multiplying the degrees of the player's rotation by the delta dt, due to the previously established fact that deg2 is always zero.
step = (math.deg(deg1) - math.deg(deg2))*dt
The following lines are equivalent to taking the player's original rotation in degrees multiplied by the delta (step) and subtracting the radians version of that off of the player's current rotation, then subtracting the delta dt from the step value, inside of a loop which is executed within a single game frame I might add. I wasn't sure if you were aware that loop is run within one frame.
player.rot = player.rot - math.rad(step)
step = step - dt
I'm not sure what your intention was with those operations, but maybe you can tell me more.
As for achieving a smoother animation, you'll need to scale down your rate and adjust how the rotation is carried out. I've rewritten your function as follows and hope that it, as an example, clarifies how this could be written better:
function rotatePlayer(dt) -- dt is seconds since last update
local current = player.rot -- player's current rotation in radians
local target = 0 -- desired angle in radians
local difference = target - current
if difference == 0 then -- nothing to be done here
return
end
local rate = math.pi / 4 -- radians turned per second (adjust as necessary)
local change = rate * dt -- r/s * delta(change in time)
if difference < 0 then
-- make change negative, since difference is as well
change = change * -1
-- If (current + change > target), settle for the lesser difference.
-- This keeps us from "overshooting" the player's rotation
-- towards a particular target.
change = math.max(change, difference)
else
change = math.min(change, difference)
end
player.rot = current + change
end
Please let me know if this resolves your problem and if you have any more questions.
I've put a change to player.lua on pastebin Stackoverflow Love2d game .
It rotates the player at a fixed rate rather than a fixed time. To do the latter just make rottime constant.
Related
I have some data that was acquired from an individual that moved a cursor from one target that was presented on a screen to another straight-ahead target that was 10 centimeters away from the original start position. I have 15 movements from this person. The data I have is the instantaneous x-position x(t) and y-position y(t)of the cursor for each movement. Because the individual did not move the mouse at the exact same speed from one movement to another, the number of samples for each movement is not the same. Below, I am showing the x-positions, y-position, and complete x-y trajectory for all movements. A link to download the data is here as well. Hopefully this gives a sense for the nature of the data. Note that the cursor position always start at the [0,0] point, but don't always exactly end at the [0,10] point.
I am tasked with linearly interpolating the x-positions of the cursor onto a vector of y-positions every 0.05cm in order to align the cursor measurements across movements (that is, I must obtain x(y). I would like to present my code below and get some feedback about whether or not I am doing this correctly:
%% Try interpolating x as a function of y (i.e., transform x(t) to x(y))
%Goal is to linearly interpolated the x-positions of the cursor onto a
%vector of y-positions every 0.05cm to align the hand path measurements
%across movements
clear all;
close all;
home;
%load the data: xpos & ypos
load('pos_data.mat')
%create cell array that will hold interpolated data
x_interp = cell(size(xpos));
y_interp = cell(size(ypos));
%construct grid vector
ypos_grid = [0:0.05:10];
num_interp_samples = length(ypos_grid);
%loop through each movement
for k=1:num_movements
%get data for the current movement
xp = xpos{k};
yp = ypos{k};
%to deal with duplicate samples, I add a very small offset to each
%sample to make them all unique
offset = cumsum([0:length(yp)-1]*1e-16);
yp2 = yp+offset(:);
%interpolate xp wrt yp
x_interp{k} = interp1(yp2, xp, ypos_grid);
%interpolate yp so that it is the same size as x_interp
t = linspace(1,length(yp2),length(yp2));
ti = linspace(1,length(yp2),num_interp_samples);
y_interp{k} = interp1(t, yp2, ti);
end
I think this should be relatively simple, but when I plot the interpolated data, it looks a bit strange to me. See below:
Namely, the trajectories seem to have lost much of its "curvature", which has me worried. Note when plotting the interpolated trajectories, I am simply doing:
figure; hold on;
for k=1:num_movements
plot(x_interp{k}, y_interp{k}, 'color', 'k');
end
xlabel('x-position (cm)');
ylabel('y-position (cm)');
title('Examples of complete trajectories (interpolated data)');
axis equal;
Here are my specific questions:
(1) Am I interpolating correctly?
(2) If the answer to (1) is yes, then am I interpreting the interpolated result correctly? Specifically, why do the shapes of the trajectories appear as they doo (lacking curvature)?
(3) Am I perhaps missing a step where after obtaining x(y), I should re-transform the data back into x(t)?
I have a series of discrete point on a plane, However, their order is scattered. Here is an instance:
To connect them with a smooth curve, I wrote a findSmoothBoundary() to achieve the smooth boundary.
Code
function findSmoothBoundary(boundaryPointSet)
%initialize the current point
currentP = boundaryPointSet(1,:);
%Create a space smoothPointsSet to store the point
smoothPointsSet = NaN*ones(length(boundaryPointSet),2);
%delete the current point from the boundaryPointSet
boundaryPointSet(1,:) = [];
ptsNum = 1; %record the number of smoothPointsSet
smoothPointsSet(ptsNum,:) = currentP;
while ~isempty(boundaryPointSet)
%ultilize the built-in knnsearch() to
%achieve the nearest point of current point
nearestPidx = knnsearch(boundaryPointSet,currentP);
currentP = boundaryPointSet(nearestPidx,:);
ptsNum = ptsNum + 1;
smoothPointsSet(ptsNum,:) = currentP;
%delete the nearest point from boundaryPointSet
boundaryPointSet(nearestPidx,:) = [];
end
%visualize the smooth boundary
plot(smoothPointsSet(:,1),smoothPointsSet(:,2))
axis equal
end
Although findSmoothBoundary() can find the smooth boundary rightly, but its efficiency is much lower ( About the data, please see here)
So I would like to know:
How to find the discrete point order effieciently?
Data
theta = linspace(0,2*pi,1000)';
boundaryPointSet= [2*sin(theta),cos(theta)];
tic;
findSmoothBoundary(boundaryPointSet)
toc;
%Elapsed time is 4.570719 seconds.
This answer is not perfect because I'll have to make a few hypothesis in order for it to work. However, for a vast majority of cases, it should works as intended. Moreover, from the link you gave in the comments, I think these hypothesis are at least weak, if not verified by definition :
1. The point form a single connected region
2. The center of mass of your points lies in the convex hull of those points
If these hypothesis are respected, you can do the following (Full code available at the end):
Step 1 : Calculate the center of mass of your points
Means=mean(boundaryPointSet);
Step 2 : Change variables to set the origin to the center of mass
boundaryPointSet(:,1)=boundaryPointSet(:,1)-Means(1);
boundaryPointSet(:,2)=boundaryPointSet(:,2)-Means(2);
Step3 : Convert coordinates to polar
[Angles,Radius]=cart2pol(boundaryPointSet(:,1),boundaryPointSet(:,2));
Step4 : Sort the Angle and use this sorting to sort the Radius
[newAngles,ids]=sort(Angles);
newRadius=Radius(ids);
Step5 : Go back to cartesian coordinates and re-add the coordinates of the center of mass:
[X,Y]=pol2cart(newAngles,newRadius);
X=X+Means(1);
Y=Y+means(2);
Full Code
%%% Find smooth boundary
fid=fopen('SmoothBoundary.txt');
A=textscan(fid,'%f %f','delimiter',',');
boundaryPointSet=cell2mat(A);
boundaryPointSet(any(isnan(boundaryPointSet),2),:)=[];
idx=randperm(size(boundaryPointSet,1));
boundaryPointSet=boundaryPointSet(idx,:);
tic
plot(boundaryPointSet(:,1),boundaryPointSet(:,2))
%% Find mean value of all parameters
Means=mean(boundaryPointSet);
%% Center values around Mean point
boundaryPointSet(:,1)=boundaryPointSet(:,1)-Means(1);
boundaryPointSet(:,2)=boundaryPointSet(:,2)-Means(2);
%% Get polar coordinates of your points
[Angles,Radius]=cart2pol(boundaryPointSet(:,1),boundaryPointSet(:,2));
[newAngles,ids]=sort(Angles);
newRadius=Radius(ids);
[X,Y]=pol2cart(newAngles,newRadius);
X=X+Means(1);
Y=Y+means(2);
toc
figure
plot(X,Y);
Note : As your values are already sorted in your input file, I had to mess it up a bit by permutating them
Outputs :
Boundary
Elapsed time is 0.131808 seconds.
Messed Input :
Output :
I am writing a simulation in which an object moves in a 2D world towards a target position, stored as vector target(x,y). The object position is stored as position vector pos(x,y) as well. The object contains two more vectors, the desired movement velocity dv(x,y), as well as the current movement velocity cv(x,y). At the start of the simulation both these velocity vectors are initial, i.e. set to (0,0).
When the object should move towards the target position, I calcuate the desired velocity vector dv, normalize it, and scale it by a movement speed value:
dv.set(target).sub(pos).normalize()
dv.scale(speed)
I want to make the movement look more realistic, that's why I use two velocity vectors. dv tells the full speed I want to move the object, and cv holds the real speed the object currently moves at.
Then at each frame (update step) the current velocity cv is set based on the desired velocity dv and an acceleration value acc. This is done by simply calculating the difference between cv and dv and clamping this difference to acc. That way the object starts to move slowly and accelerates gradually to eventually reach full speed.
So far this is working fine. Now I want to make use of acc for deceleration as well. When the distance between pos and target is at a certain value, the desired velocity dv should be set to (0,0), so that the object gradually decelerates until it comes to a full stop at the target position.
My question is: How can I calculate at which distance I need to set dv to (0,0) (i.e. tell the system to stop movement), so that the object decelerates correctly to stop exactly at the target position?
Use the kinematic equations:
vf2 = vi2 + 2 * a * d
vf is your final velocity, or 0 (the speed you want to be going)
vi is your initial velocity, given (the speed your object is currently moving).
a is acceleration
d is distance.
Solve for d:
2*a*d = vf2 - vi2
2*a*d = 0 - vi2
assume acceleration is negative, so multiply both sides by -1
2*|a|*d = vi2
|a| is the absolute value of your acceleration (deceleration in your case)
d = vi2 / (2*|a|)
You're doing a discrete time simulation of motion. One way to keep things simple is to perform the calculations in a way that makes acceleration and deceleration symmetrical. In other words, the distance traveled while accelerating should be the same as the distance traveled while decelerating. As an example, assume
acceleration is 5
top speed is 13
the object begins decelerating as soon as it reaches top speed
Here's how the discrete time simulation would progress
first tick
old speed = 0
new speed = 5
distance = 5
second tick
old speed = 5
new speed = 10
distance = 15
third tick
old speed = 10
new speed = 13
distance = 28 <-- total distance while accelerating
fourth tick
old speed = 13
distance = 41
new speed = 10 <-- not 8!!!
fifth tick
old speed = 10
distance = 51
new speed = 5
sixth tick
old speed = 5
distance = 56 <-- Yay, twice the distance, we have symmetry
new speed = 0
There are two key points here
While accelerating the speed is updated first and then the distance is updated. While decelerating the order is reversed, the distance is updated first, and then the speed.
When decelerating, it's important to keep the adjusted speed as a multiple of the acceleration
In the C programming language, the following code could be used to update the speed during deceleration
if ( old_speed % acceleration != 0 ) // if speed is not a multiple of acceleration
new_speed = old_speed - old_speed % acceleration; // reduce speed to a multiple of acceleration
else // otherwise
new_speed = old_speed - acceleration; // reduce speed by acceleration
If acceleration and deceleration are symmetrical, then computing the deceleration distance is the same as computing the acceleration distance.
distance = acceleration * (1+2+3+ ... +N) + fudge_factor
where
N is top_speed / acceleration truncated to an integer, e.g. 13/5 ==> 2
fudge_factor is 0 if top speed is a multiple of acceleration, or
top_speed otherwise
The computation can be simplified by noting that
1+2+3+ ... +N = N * (N+1) / 2
In C, the total distance travelled while decelerating could be computed as follows
int top_speed = 13;
int acceleration = 5;
int N = top_speed / acceleration; // Note: in C, integer division truncates
int fudge = 0;
if ( top_speed % acceleration != 0 )
fudge = top_speed;
int distance = acceleration * (N * (N+1)) / 2 + fudge;
I have a point that moves (in one dimension), and I need it to move smoothly. So I think that it's velocity has to be a continuous function and I need to control the acceleration and then calculate it's velocity and position.
The algorithm doesn't seem something obvious to me, but I guess this must be a common problem, I just can't find the solution.
Notes:
The final destination of the object may change while it's moving and the movement needs to be smooth anyway.
I guess that a naive implementation would produce bouncing, and I need to avoid that.
This is a perfect candidate for using a "critically damped spring".
Conceptually you attach the point to the target point with a spring, or piece of elastic. The spring is damped so that you get no 'bouncing'. You can control how fast the system reacts by changing a constant called the "SpringConstant". This is essentially how strong the piece of elastic is.
Basically you apply two forces to the position, then integrate this over time. The first force is that applied by the spring, Fs = SpringConstant * DistanceToTarget. The second is the damping force, Fd = -CurrentVelocity * 2 * sqrt( SpringConstant ).
The CurrentVelocity forms part of the state of the system, and can be initialised to zero.
In each step, you multiply the sum of these two forces by the time step. This gives you the change of the value of the CurrentVelocity. Multiply this by the time step again and it will give you the displacement.
We add this to the actual position of the point.
In C++ code:
float CriticallyDampedSpring( float a_Target,
float a_Current,
float & a_Velocity,
float a_TimeStep )
{
float currentToTarget = a_Target - a_Current;
float springForce = currentToTarget * SPRING_CONSTANT;
float dampingForce = -a_Velocity * 2 * sqrt( SPRING_CONSTANT );
float force = springForce + dampingForce;
a_Velocity += force * a_TimeStep;
float displacement = a_Velocity * a_TimeStep;
return a_Current + displacement;
}
In systems I was working with a value of around 5 was a good point to start experimenting with the value of the spring constant. Set it too high will result in too fast a reaction, and too low the point will react too slowly.
Note, you might be best to make a class that keeps the velocity state rather than have to pass it into the function over and over.
I hope this is helpful, good luck :)
EDIT: In case it's useful for others, it's easy to apply this to 2 or 3 dimensions. In this case you can just apply the CriticallyDampedSpring independently once for each dimension. Depending on the motion you want you might find it better to work in polar coordinates (for 2D), or spherical coordinates (for 3D).
I'd do something like Alex Deem's answer for trajectory planning, but with limits on force and velocity:
In pseudocode:
xtarget: target position
vtarget: target velocity*
x: object position
v: object velocity
dt: timestep
F = Ki * (xtarget-x) + Kp * (vtarget-v);
F = clipMagnitude(F, Fmax);
v = v + F * dt;
v = clipMagnitude(v, vmax);
x = x + v * dt;
clipMagnitude(y, ymax):
r = magnitude(y) / ymax
if (r <= 1)
return y;
else
return y * (1/r);
where Ki and Kp are tuning constants, Fmax and vmax are maximum force and velocity. This should work for 1-D, 2-D, or 3-D situations (magnitude(y) = abs(y) in 1-D, otherwise use vector magnitude).
It's not quite clear exactly what you're after, but I'm going to assume the following:
There is some maximum acceleration;
You want the object to have stopped moving when it reaches the destination;
Unlike velocity, you do not require acceleration to be continuous.
Let A be the maximum acceleration (by which I mean the acceleration is always between -A and A).
The equation you want is
v_f^2 = v_i^2 + 2 a d
where v_f = 0 is the final velocity, v_i is the initial (current) velocity, and d is the distance to the destination (when you switch from acceleration A to acceleration -A -- that is, from speeding up to slowing down; here I'm assuming d is positive).
Solving:
d = v_i^2 / (2A)
is the distance. (The negatives cancel).
If the current distance remaining is greater than d, speed up as quickly as possible. Otherwise, begin slowing down.
Let's say you update the object's position every t_step seconds. Then:
new_position = old_position + old_velocity * t_step + (1/2)a(t_step)^2
new_velocity = old_velocity + a * t_step.
If the destination is between new_position and old_position (i.e., the object reached its destination in between updates), simply set new_position = destination.
You need an easing formula, which you would call at a set interval, passing in the time elapsed, start point, end point and duration you want the animation to be.
Doing time-based calculations will account for slow clients and other random hiccups. Since it calculates on time elapsed vs. the time in which it has to compkete, it will account for slow intervals between calls when returning how far along your point should be in the animation.
The jquery.easing plugin has a ton of easing functions you can look at:
http://gsgd.co.uk/sandbox/jquery/easing/
I've found it best to pass in 0 and 1 as my start and end point, since it will return a floating point between the two, you can easily apply it to the real value you are modifying using multiplication.
I'm currently developing a script for a map in COD4. I think that the language is so simple that I'm tagging this as language-agnostic since the problem is in the algorithm for this situation.
There is a room which is 960 units wide. And inside it there's an object in the middle, which we'll count as the axis. The ball is supposed to move to a random position each time it is hit, but should not traverse further than the walls. Here's a diagram:
The API of the game only allows the moving of objects relative to its position, as far as I know, so here's code that I came up with. The problem is that after the second call to head_move() it begins to produce unexpected results and this is crashing my head. Could somebody help me out?
movementThink():
while (1)
{
self waittill ("trigger", player); //Wait till player hits the object
head_origin thread head_move();
}
head_move()
{
/* level.prevx is a global variable which I use to store
the distance traveled in the previous shot. Defaults to 0 */
/*This works in the first and second hit, but then it begins to show
incorrect max and min values*/
x_min = (0-480) + level.prevx;
x_max = x_min + 960;
x_units = RandomIntRange( x_min, x_max ); //Create a random integrer
log2screen("MIN: " + x_min + " and MAX: " + x_max + " and MOVED " + x_units);
log2screen("Moved " + x_units);
//Movement function, first parameter is the distance to be traveled, and the second one is the speed
self movex (x_units , level.movespeed);
level.prevx = x_units;
}
EDIT: Just to clarify. When the user shoots the ball, its position changes to a certain value. Now, if he hits it again, the min and max values of the random int generator should change to prevent the ball from moving outside the walls. Example:
Level starts. The ball is in the middle of the room. The min and max ranges are -480 and 480 respectively
The user hits the ball and its moved -200 units (200 units to the left).
Now, the min and max range should be -280 and 680.
I hope this is clear enough.
EDIT 2: Edited the sign as FlipScript suggested. Here's the output from the log2screen functions, what is actually happening:
MIN: -480 and MAX 480. MOVED 67
MIN: -413 and MAX 547. MOVED 236
MIN: -244 and MAX 716. MOVED 461
Just a sample case. Something is backwards I believe, these aren't the right calculations to do.
Your code works only when self.prevx contains your displacement from the starting position, i.e. your absolute position. However, what you are storing is your displacement from your current position. It works the first two times because that displacement happens to be the same as your absolute position, but once you move again, you lose all track of where you are.
What you should do instead is get rid of min and max, and start by calculating a random absolute position within the bounds. Then use your previously stored absolute position to calculate the relative movement needed to get you there, and store the new absolute position.
head_move()
{
new_x = RandomIntRange( -480, 480 ); //create a random location
delta_x = new_x - level.prev; //determine relative movement needed to get there
self movex (delta_x , level.movespeed); //move to new position
level.prevx = new_x; //store new position
}
I dont know much about the programming environment, but this line
head_origin thread head_move();
is suspicious for troublemaking. What are these tokens? Anything that says thread could be duplicating data structures and throwing your local variables astray.
And why do x_min and x_max change? Where's y_min and y_max?
Something doesn't look right in this line here:
x_max = x_min - 960;
Is the MAX really the MIN MINUS 960? From your description, it sounds like that should be a '+' sign.
EDIT:
In your additional comments, the minus sign wouldn't allow these truths:
Level starts....The min and max ranges are -480 and 480 respectively
...
Now, the min and max range should be -280 and 680.
Comments 1 and 3 point to that sign needing to be '+' sign.