I've got some jerky movement of my sprite.
Basically, when the user touches a point on the screen, the sprite should move to that point. This is working mostly fine... it's even taking into account a delta - because frame rate may not be consistant.
However, I notice that the y movement usually finishes before the x movement (even when the distances to travel are the same), so it appears like the sprite is moving in an 'L' shape rather than a smooth diagonal line.
Vertical and horizontal velocity (vx, vy) are both set to 300. Any ideas what's wrong? How can I go about getting my sprite to move in a smooth diagonal line?
- (void)update:(ccTime)dt
{
int x = self.position.x;
int y = self.position.y;
//if ball is to the left of target point
if (x<targetx)
{
//if movement of the ball won't take it to it's target position
if (x+(vx *dt) < targetx)
{
x += vx * dt;
}
else {
x = targetx;
}
} else if (x>targetx) //same with x being too far to the right
{
if (x-(vx *dt) > targetx)
{
x -= vx * dt;
}
else {
x = targetx;
}
}
if (y<targety)
{
if (y+(vy*dt)<targety)
{
y += vy * dt;
}
else {
y = targety;
}
} else if (y>targety)
{
if (y-(vy*dt)>targety)
{
y -= vy * dt;
}
else {
y = targety;
}
}
self.position = ccp(x,y);
}
You want to move to (targetx, targety) from any (x,y) and arrive at both coordinates at the same time (to avoid the "dogleg"). So, suppose the x velocity is vx and you get there in t seconds. That means vx = (targetx - x)/t. t must be the same for the y coordinate if you want smooth movement to the same point at the same time, so that means t = (targetx - x)/vx and vy must actually be (targety - y)*vx/(targetx - x).
In other words, you can't set vx and vy separately and get the result you want.
Related
I can use the function CheckCollisionCircleRec(Vector2{ x, y }, radius, paddleRect) to find out simply if my circle has collided with my rectangle, but I want to be able to find out what side of the rectangle my circle has collided with. How would I go about doing this? None of the algorithms I've made are working. Example of my most recent blunder:
if (x - radius <= 0 || x + radius >= screenWidth) {
speedX *= -1;
}
else if (y - radius <= 0 || y + radius >= screenHeight) {
speedY *= -1;
}
else if (CheckCollisionCircleRec(Vector2{ x, y }, radius, paddleRect)) {
float paddleBottom = paddleRect.y + paddleRect.height;
float paddleRight = paddleRect.x + paddleRect.width;
if (range(paddleRect.x, paddleRect.x + speedX / 100, x + radius)) {
x = paddleRect.x - radius;
speedX *= -1;
}
if (range(paddleRight - speedX / 100, paddleRight, x - radius)) {
x = paddleRight + radius;
speedX *= -1;
};
if (range(paddleRect.y, paddleRect.y + speedY / 100, y + radius)) {
y = paddleRect.y - radius;
speedY *= -1;
}
if (range(paddleBottom - speedY / 100, paddleBottom, y - radius)) {
y = paddleBottom + radius;
speedY *= -1;
};
EDIT:
Here's the function I used to get the working end result:
// px and py are the ball's previous locations
// x and y are the ball's current locations
void checkCollision(Rectangle rectangle) {
int left = rectangle.x;
int right = rectangle.x + rectangle.width;
int top = rectangle.y;
int bottom = rectangle.y + rectangle.height;
if (CheckCollisionCircleRec(Vector2{ x, y }, radius, rectangle)) {
if (px < left) {
speedX = negative(speedX);
x = left - radius;
}
else if (px > right) {
speedX = positive(speedX);
x = right + radius;
}
else if (py < top) {
speedY = negative(speedY);
y = top - radius;
}
else if (py > bottom) {
speedY = positive(speedY);
y = bottom + radius;
};
};
};
A simply way is to use the PREVIOUS location of your circle. Not sure if you can in your program, but since you have an x and y handy, I'll assume you can have a prevX and prevY. I'll also assume these values represent the CENTER of the circle.
Now if (prevX < paddleRect.x), then you likely collided with the left side (not guaranteed, but resolving ambiguities with complete accuracy requires recursively simulating your physics at smaller and smaller timesteps, which is likely unnecessary here). You can also constrain this more tightly with something like if (prevX < paddleRect.x && prevY > paddleRect.y && prevY < paddleRect.y + paddRect.height). There are various constraints you can add depending on how cleanly you want the side to be hit before detecting it. You can add corner hits, etc.
The reason for using the previous location is that, if your circle is moving fast enough, then in a single frame it can jump straight into the middle of the rectangle. It's usually necessary to use the previous position to give more specific collision information in the current-location collision
void update(RenderWindow& window)
{
if (Keyboard::isKeyPressed(Keyboard::W))
{
dy = -0.3;
}
if (Keyboard::isKeyPressed(Keyboard::A))
{
dx = -0.3;
}
if (Keyboard::isKeyPressed(Keyboard::S))
{
dy = 0.3;
}
if (Keyboard::isKeyPressed(Keyboard::D))
{
dx = 0.3;
}
x += dx;
y += dy;
dx = dy = 0;
EntitySprite.setPosition(x, y);
window.draw(EntitySprite);
}
When the movement is described in such a code, the player moves angularly: either to the left, or up, or to the right, or down, or diagonally at an angle of 45 degrees by pressing two buttons together, for example S and D. Can this angle be made smoother so that the movement itself was carried out not only to the left, right, diagonally, etc.? My knowledge of geometry is not enough here, so I ask for your help).
In your code, the user input directly modifies the player position. This is probably the reason why the player movement looks so abrupt to you. Technically, in your code, the user input is what is determining the player velocity at any given moment.
A more realistic approach would be to let the player have a velocity property instead –
that represents the player position's rate of change – and then have the player position updated only through this velocity, not directly from the user input. Instead, the velocity would be what is directly modified by the input, but not entirely determined by the current input as it will also depend on its previous value.
Following this approach, the user input is used to calculate the player acceleration in each call to update(). This acceleration – the rate of change of the velocity – is used to update the player velocity directly. Finally, the player velocity is, in turn, used to update the player position.
The following code implements this approach by introducing the velocity_ data member and the acceleration local variable:
void update(RenderWindow& window) {
sf::Vector2f acceleration;
// adjust this at will
const float dAcc = 0.3f;
// set acceleration
if (Keyboard::isKeyPressed(Keyboard::W))
acceleration.y -= dAcc;
if (Keyboard::isKeyPressed(Keyboard::A))
acceleration.x -= dAcc;
if (Keyboard::isKeyPressed(Keyboard::S))
acceleration.y += dAcc;
if (Keyboard::isKeyPressed(Keyboard::D))
acceleration.x += dAcc;
// update velocity through acceleration
velocity_ += acceleration;
// update position through velocity
x += velocity_.x;
y += velocity_.y;
// apply damping to the velocity
velocity_ = 0.99f * velocity_;
EntitySprite.setPosition(x, y);
window.draw(EntitySprite);
};
This way, the player possesses some kind of inertia, and its movement looks smoother.
Note that you may want to have some damping for the velocity as in:
velocity_ = 0.99f * velocity_;
This will resemble the effect of drag forces.
with help of that man I did it like this, but some better)
void update(RenderWindow& window)
{
float decceleration = 0.3;
if (Keyboard::isKeyPressed(Keyboard::W))
{
accelerationY -= decceleration;
}
if (Keyboard::isKeyPressed(Keyboard::S))
{
accelerationY += decceleration;
}
if (Keyboard::isKeyPressed(Keyboard::A))
{
accelerationX -= decceleration;
}
if (Keyboard::isKeyPressed(Keyboard::D))
{
accelerationX += decceleration;
}
dx += accelerationX;
dy += accelerationY;
speed = sqrt(dx * dx + dy * dy);
if (speed > maxSpeed)
{
dx *= maxSpeed / speed;
dy *= maxSpeed / speed;
}
x += dx;
y += dy;
dx *= 0.9;
dy *= 0.9;
accelerationX = 0;
accelerationY = 0;
EntitySprite.setPosition(x, y);
window.draw(EntitySprite);
}
I have to move a ball in an angle in an open SFML and keep it within the window size (Like the DVD thing), but my current function makes it to the bottom and doesn't "bounce". It slides across the bottom and stops once it reaches the other corner. The initial position is (1,1)
void Bubble::updatePosition() {
if( isTopBottom() ){
do{
_x += .1;
_y += -.2;
}while( !isTopBottom() );
}
else if( isLeftRight() ){
do{
_x += -.1;
_y += .2;
}while( !isLeftRight() );
}
else{
_x += .1;
_y += .2;
}
_bubble.setPosition(_x, _y);
}
the isLeftRight, isTopBottom are bools that check if they have reached the edges
Simple Solution
Use velocities and manipulate those on collision; then, use the velocity to update the position.
Check each edge separately and decide on one relevant velocity component based on that.
e.g. (following your values)
// Positions:
float x = 1.f;
float y = 1.f;
// Velocities:
float vx = 0.1f;
float vy = 0.2f;
// ... then, inside loop:
// Check collisions (and adjust velocity):
if (x < 0.f)
vx = 0.1f;
else if (x > 640.f)
vx = -0.1f;
if (y < 0.f)
vy = 0.2f;
else if (y > 640.f)
vy = -0.2f;
// update position (still inside loop):
x += vx;
y += vy;
Cleaner Solution
This is the same as the simple solution above but, since you tagged SFML, you can use SFML vectors to keep the two components together. Also modified variable names to be more clear. Pulled out the size of the window and the velocity amounts from being hard-coded into the logic as well:
const sf::Vector2f windowSize(640.f, 640.f);
const sf::Vector2f velocityAmount(0.1f, 0.2f);
sf::Vector2f position(1.f, 1.f);
sf::Vector2f velocity = velocityAmount;
// ... then, inside loop:
// Check collisions (and adjust velocity):
if (position.x < 0.f)
velocity.x = velocityAmount.x;
else if (position.x > windowSize.x)
velocity.x = -velocityAmount.x;
if (position.y < 0.f)
velocity.y = velocityAmount.y;
else if (position.y > windowSize.y)
velocity.y = -velocityAmount.y;
// update position (still inside loop):
position += velocity;
You should notice that the velocity is the values that are added to the position on each iteration of the loop and that velocity does not change when it is not considered colliding with an edge.
Initial Problem
The initial problem you had is it always moves in the same direction (towards the bottom-right) if it is not hitting an edge. This means that it'll never be allowed to rise above the bottom edge (or away from right edge).
I'm supposed to write a code that simulates an inelastic particle bouncing within a wall and a semicircle (radius 30, center # (50,0). The particle loses 3% of its original velocity every time it hits a wall and/or the semicircle.
I'm also using graphics.h library
I have made most of the program already, but the particle does not properly bounce on the semicircle.
I used the reflection matrix:
\begin{bmatrix}
cos 2\theta & sin2\theta \
sin2 \theta & -cos2\theta
\end{bmatrix}
here is the this snippet of the code containing the conditions when the particle hits the semicircle:
//initial conditions
x = 0;
y = 100;
Vx = 15.0;
Vy = 0.0;
double dt = 0.0001; //initial time step and increment
for(double t = 0; t < 50; t += dt)
{
Vy = Vy + grav*dt;
//Conditions when the particle hits a wall or the floor and retaining only 97% of its speed
if (y<0.0)
{
Vy = -Vy + grav*dt;
}
if(x >100.0 || x < 0.0)
{
Vx = -Vx;
}
//condition when the particle hits the semicircle
//semicircle equation: y = sqrt(30^2 - (x-50)^2)
if(y < sqrt((30*30)-((x-50)*(x-50))) )
{
Vxnew = ((Vx)*(cos(2.0*theta)) + (Vy*(sin(2.0*theta))));
Vy = (Vy*(cos(2.0*theta)) - ((Vx)*(sin(2.0*theta))));
Vx = Vxnew;
}
x = x + Vx*dt;
y = y + Vy*dt;
putpixel(conx(x), cony(y), 15);
}
I'm not sure if this reflection matrix applies on curved surfaces and I should use something else or my implementation is wrong.
For the losing the velocity part, I can probably just multiply the new x and y velocities by 0.97.
For context, this function that I made converts the calculated value in the loop to pixel values
//convert to pixel value (scale of 6)
double conx(double x)
{
return x * (600/100) + 50;
}
double cony(double y)
{
return -y * (600/100) + 650;
}
Here is what I'm trying to do. I'm trying to make a bullet out of the center of the screen. I have an x and y rotation angle. The problem is the Y (which is modified by rotation on the x) is really not working as intended. Here is what I have.
float yrotrad, xrotrad;
yrotrad = (Camera.roty / 180.0f * 3.141592654f);
xrotrad = (Camera.rotx / 180.0f * 3.141592654f);
Vertex3f Pos;
// get camera position
pls.x = Camera.x;
pls.y = Camera.y;
pls.z = Camera.z;
for(float i = 0; i < 60; i++)
{
//add the rotation vector
pls.x += float(sin(yrotrad)) ;
pls.z -= float(cos(yrotrad)) ;
pls.y += float(sin(twopi - xrotrad));
//translate camera coords to cube coords
Pos.x = ceil(pls.x / 3);
Pos.y = ceil((pls.y) / 3);
Pos.z = ceil(pls.z / 3);
if(!CubeIsEmpty(Pos.x,Pos.y,Pos.z)) //remove first cube that made contact
{
delete GetCube(Pos.x,Pos.y,Pos.z);
SetCube(0,Pos.x,Pos.y,Pos.z);
return;
}
}
This is almost identical to how I move the player, I add the directional vector to the camera then find which cube the player is on. If I remove the pls.y += float(sin(twopi - xrotrad)); then I clearly see that on the X and Z, everything is pointing as it should. When I add pls.y += float(sin(twopi - xrotrad)); then it almost works, but not quite, what I observed from rendering out spheres of the trajector is that the furthur up or down I look, the more offset it becomes rather than stay alligned to the camera's center. What am I doing wrong?
Thanks
What basically happens is very difficult to explain, I'd expect the bullet at time 0 to always be at the center of the screen, but it behaves oddly. If i'm looking straight at the horizon to +- 20 degrees upward its fine but then it starts not following any more.
I set up my matrix like this:
void CCubeGame::SetCameraMatrix()
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(Camera.rotx,1,0,0);
glRotatef(Camera.roty,0,1,0);
glRotatef(Camera.rotz,0,0,1);
glTranslatef(-Camera.x , -Camera.y,-Camera.z );
}
and change the angle like this:
void CCubeGame::MouseMove(int x, int y)
{
if(!isTrapped)
return;
int diffx = x-lastMouse.x;
int diffy = y-lastMouse.y;
lastMouse.x = x;
lastMouse.y = y;
Camera.rotx += (float) diffy * 0.2;
Camera.roty += (float) diffx * 0.2;
if(Camera.rotx > 90)
{
Camera.rotx = 90;
}
if(Camera.rotx < -90)
{
Camera.rotx = -90;
}
if(isTrapped)
if (fabs(ScreenDimensions.x/2 - x) > 1 || fabs(ScreenDimensions.y/2 - y) > 1) {
resetPointer();
}
}
You need to scale X and Z by cos(xradrot). (In other words, multiply by cos(xradrot)).
Imagine you're pointing straight down the Z axis but looking straight up. You don't want the bullet to shoot down the Z axis at all, this is why you need to scale it. (It's basically the same thing that you're doing between X and Z, but now doing it on the XZ vector and Y.)
pls.x += float(sin(yrotrad)*cos(xrotrad)) ;
pls.z -= float(cos(yrotrad)*cos(xrotrad)) ;
pls.y += float(sin(twopi - xrotrad));