Gravity not working correctly - lua

My code for physics in my game is this:
-- dt = time after each update
self.x = math.floor(self.x + math.sin(self.angle)*self.speed*dt)
self.y = math.floor(self.y - math.cos(self.angle)*self.speed*dt)
-- addVector(speed,angle,speed2,angle2)
self.speed,self.angle = addVector(self.speed,self.angle,g,math.pi)`
when it hits the ground, the code for it to bounce is :
self.angle = math.pi - self.angle
self.y = other.y - self.r`
the function addVector is defined here:
x = math.sin(angle)*speed + math.sin(angle2)*speed2
y = math.cos(angle)*speed + math.cos(angle2)*speed2
v = math.sqrt(x^2 + y^2)
a = math.pi/2 - math.atan(y,x)
return v,a
but when I place a single ball in the simulation without any drag or elasticity, the height of the ball after each bounce keeps getting higher. Any idea what may be causing the problem?
Edit: self.r is the radius, self.x and self.y are the position of the centre of the ball.

Your Y axis is increasing downward and decreasing upward.
Making self.y = math.floor(..) moves your ball a bit upward every frame.
The solution is to store your coordinates with maximal precision.
You could make new variable y_for_drawing = math.floor(y) to draw the ball at pixel with integer coordinates, but your main y value must have fractional part.

I’ve managed to get your code to run and reproduce the behavior you are seeing. I also find it difficult to figure out the issue. Here’s why: movement physics involves position, which is affected by a velocity vector, which in turn is affected by an acceleration vector. In your code these are all there, but are in no way clearly separated. There are trig functions and floor functions all interacting in a way that makes it difficult to see what role they are playing in the final position calculation.
By far the best and easiest-to-understand tutorial to help you implement basic physics lime this is The Nature of Code (free to read online, with interactive examples). As an exercise I ported most of the early exercises into Lua. I would suggest you see how he clearly separates the position, velocity and acceleration calculations.
As an experiemnt, increase g to a much higher number. When I did that, I noticed the ball would eventually settle to the ground, but of course the bouces were too fast and it didnt bounce in a way that seems natural.
Also, define other.y - it doesnt seem to affect the bouncing issue, but just to be clear on what that is.

Related

How to change the velocity in each frame in Love2D?

I'm trying to code a Pong game on Lua using the framework Love2D with some extra features. Among others, I want curves to occur. In order to do so, I'm trying to implement the trajectory of horizontally launched projectiles. I have a ball table with a position attribute for x (ball.x) and another for y (ball.y). I, also, have a an attribute for the x velocity (ball.dx) and another for the y velocity (ball.dy). Finally, I have an acceleration variable (gravity)
In my game, if the paddle moves and the ball hits it, the ball should follow and horizontal curve. In order to create my curves, I want to change the y-axis velocity on each frame in order to make my ball move in an arc across the screen. The main issue that I have is that I don't know how to change this velocity in each frame in order to create the expected arc. The most recent attempt that I made was to create a while loop like the following code. However, it creates an infinite loop. Can someone enlighten me please?
Clarification:
-Player.x and player.y are the player coordinate
-Player2.x and Player2.y are the opponent coordinate
-This piece of code is inside another if statement detecting a collision. It is inside the love.update(dt) function.
Thank you so much!
if love.keyboard.isDown("up") and distanceBetween(ball.x,ball.y,player.x,player.y)>30 then
ball.dy=0
while CollisionDetector(ball.x,player2.x,ball.y,player2.y, player2.width,player2.height,ball.size)==false or ball.x>0 or ball.y-20>0 or ball.y+20<love.graphics.getHeight() do
ball.dy=ball.dy+gravity*dt
ball.y=ball.y+ball.dy
end
end
I believe the snippet you've provided is a part of love.update function. (At least it should be.) That function is supposed to perform a single step of the game at a time and return. It is not supposed to handle the whole flight in a single call.
In your particular case, while loop expects the ball to move, but that is not something that will happen until the loop and encompassing function end.
In general, to simulate ongoing processes you'll have to store the information about such process. For example, if you want gravity be dependent on the paddle movement direction, then you'll have to modify the corresponding variable and store it between the call.
In your code, there are multiple other design flaws that make it do not what you think it should do. With some functions and code parts left to be implemented by yourself, the code outline would look as follows:
function collides(ball,player)
return (ball.x,player.x,ball.y,player.y, player.width,player.height,ball.size)
end
function love.update(dt)
handle_paddle_movements()
--handle the ball-paddle interaction
if collides(ball,player1) or collides(ball,player2) then
ball.dx=-ball.dx
if love.keyboard.isDown("up") then
--store the info you need to know about the interaction
ball.d2y = -gravity
else if love.keyboard.isDown("down")
ball.d2y = gravity
else
ball.d2y = 0
end
end
handle_the_wall_collision()
--ball movement code should be separate from collision handling
--concrete equations can be whatever you want
ball.x = ball.x+ball.dx*dt
ball.dy = ball.dy + ball.d2y * dt
ball.y = ball.y + ball.dy * dt
end

Making a rectangle jump? (Love2d)

I might be stupid or something. But, I'm having a hard time with this. I usually find examples of code but it just confuses me. Unfortunately there isn't any good tutorial for this. I been using Lua for almost a year so I kinda have experience. Help would be extremely appreciate!
Basically I want to learn how to make rectangle jump up and then go back down.
For a single block that you control, you essentially need to store it's gravity and figure out a nice acceleration for it.
currentGravity = 0;
gravity = 1;
Then in the loop, you have to check if it's on ground using some collision detection. You want to add the gravity acceleration to currentGravity:
currentGravity = currentGravity + gravity
Then, you add it to the current y axis of the block:
YAxis = YAxis + currentGravity
Once you land, make sure to set gravity to 0. Also be sure to keep setting it to 0 to ensure you don't fall through the ground (as you kept adding to gravity no matter what.)
if not inAir() then
currentGravity = 0
end
And, of course, to jump, set currentGravity to a negative number (like 20) (if that's how you made the gravity work.)
Here's a collision detection function I made for Love2D:
function checkCollision(Pos1,Size1,Pos2,Size2)
local W1,H1,W2,H2 = Size1[1]/2,Size1[2]/2,Size2[1]/2,Size2[2]/2
local Center1,Center2 = {Pos1[1]+W1,Pos1[2]+H1},{Pos2[1]+W2,Pos2[2]+H2}
local c1 = Center1[1]+(W1) > Center2[1]-W2
local c2 = Center1[1]-(W1) < Center2[1]+W2
local c3 = Center1[2]+(H1) > Center2[2]-H2
local c4 = Center1[2]-(H1) < Center2[2]+H2
if (c1 and c2) and (c3 and c4) then
return true
end
return false
end
It assumes the position you gave it is the center of the block. If the box is rotated it won't work. You'll have to figure out how to make it work with walls and the like. And yes, it's ugly because it's very old. :p

Changing a moving objects direction of travel in corona

I'm new to Corona and looking for a little help manipulating moving objects:
Basically I want a set up where when I can click on a moving object, a dialog box will pop up giving me the option to change the speed of the object and the vector of travel. I'm pretty sure I can figure out the event handling and the dialog but I'm stuck on simply changing the direction of travel to the new vector
in a simple example, I have a rect moving up the screen as follows:
obj1 = display.newRect(500, 800, 10, 40)
transition.to(obj1,{x=500, y = 100, time = 40000})
I know I can change the speed by adjusting the time, but if I use
obj1:rotate(30)
to turn the object 30 degrees, how do I make it travel in the new direction?
Should I be using physics - linear impulse for example, instead of transitions?
Apologies if this is a stupid question but I have searched without success for a solution.
This sounds like what you are trying to do. You would have to modify bits to fit your code but this is a working example. So if you copy it to a new main.lua file and run it you can see how it works. (Click to rotate obj).
local obj = display.newRect(50,50, 10, 40)
local SPEED = 1
local function move(event)
obj.x = obj.x + math.cos(math.rad(obj.rotation)) * SPEED
obj.y = obj.y + math.sin(math.rad(obj.rotation)) * SPEED
end
local function rotate(event)
obj.rotation = obj.rotation + 45
end
Runtime:addEventListener("enterFrame", move)
Runtime:addEventListener("tap", rotate)
Basically I used the "enterFrame" event to 'move' the rectangle, by recalculating the x and y of the objects location using its rotation (which is easy enough to modify) every frame.

Quaternions, rotate a model and align with a direction

Suppose you have quaternion that describes the rotation of a 3D Model.
What I want to do is, given an Object (with rotationQuaternion, side vector...), I want to align it to a target point.
For a spaceship, I want the cockpit to point to a target.
Here is some code I have ... It's not doing what I want and I don't know why...
if (_target._ray.Position != _obj._ray.Position)
{
Vector3 vec = Vector3.Normalize(_target._ray.Position - _obj._ray.Position);
float angle = (float)Math.Acos(Vector3.Dot(vec, _obj._ray.Direction));
Vector3 cross = Vector3.Cross(vec, _obj._ray.Direction);
if (cross == Vector3.Zero)
cross = _obj._side;
_obj._rotationQuaternion *= Quaternion.CreateFromAxisAngle(cross,angle);
}
// Updates direction, up, side vectors and model Matrix
_obj.UpdateMatrix();
after some time the rotationQuaternion is filled with almost Zero at X,Y,Z and W
Any help?
Thanks ;-)
This is a shortcut I've used to get the quaternion for lock-on-target rotation:
Matrix rot = Matrix.CreateLookAt(_arrow.Position, _cube.Position, Vector3.Down);
_arrow.Rotation = Quaternion.CreateFromRotationMatrix(rot);
For this example, I'm rendering an arrow and a cube, where the cube is moving around in a circle, and with the above code the arrow is always pointing at the cube. (Though I imagine there are some edge cases when cube is exactly above or below).
Once you get this quaternion (from spaceship to target), you can use Quaternion.Lerp() to interpolate between current ship rotation and the aligned one. This will give your rotation a smooth transition (not just snap to target).
Btw, might be that your rotation gets reduced to zero because you're using *= when assigning to it.
Your code's a bit funky.
if (_target._ray.Position != _obj._ray.Position)
{
This may or may not be correct. Clearly, you've overridden the equals comparator. The correct thing be be doing here would be to ensure that the dot-product between the two (unit-length) rays is close to 1. If the rays have the same origin, then presumably have equal 'positions' means they're the same.
Vector3 vec = Vector3.Normalize(_target._ray.Position - _obj._ray.Position);
This seems particularly wrong. Unless the minus operator has been overridden in a strange way, subtracting this way doesn't make sense.
Here's pseudocode for what I recommend:
normalize3(targetRay);
normalize3(objectRay);
angleDif = acos(dotProduct(targetRay,objectRay));
if (angleDif!=0) {
orthoRay = crossProduct(objectRay,targetRay);
normalize3(orthoRay);
deltaQ = quaternionFromAxisAngle(orthoRay,angleDif);
rotationQuaternion = deltaQ*rotationQuaternion;
normalize4(rotationQuaternion);
}
Two things to note here:
Quaternions are not commutative. I've assumed that your quaternions are rotating column vectors; so I put deltaQ on the left. It's not clear what your *= operator is doing.
It's important to regularly normalize your quaternions after multiplication. Otherwise small errors accumulate and they drift away from unit length causing all manner of grief.
OMG! It worked!!!
Vector3 targetRay = Vector3.Normalize(_target._ray.Position - _obj._ray.Position);
Vector3 objectRay = Vector3.Normalize(_obj._ray.Direction);
float angle = (float)Math.Acos(Vector3.Dot(targetRay, objectRay));
if (angle!=0)
{
Vector3 ortho = Vector3.Normalize(Vector3.Cross(objectRay, targetRay));
_obj._rotationQuaternion = Quaternion.CreateFromAxisAngle(ortho, angle) * _obj._rotationQuaternion;
_obj._rotationQuaternion.Normalize();
}
_obj.UpdateMatrix();
Thank you very much JCooper!!!
And niko I like the idea of Lerp ;-)

Corona SDK: How to make object move forward?

I have a physics body, and I want it to move forward in the direction that it is facing. I'm only thirteen which I hope explains why I'm so bad at trigonometry. Can anyone tell me how to do this in Corona?
I'm gonna assume you want to push your object with a force. Either way we'll need to get an x and y component of the direction your body is facing. Here's how to get the x and y from the rotation angle:
-- body is your physics body
local angle = math.rad(body.rotation) -- we need angle in radians
local xComp = math.cos(angle) -- the x component
local yComp = -math.sin(angle) -- the y component is negative because
-- "up" the screen is negative
(note: if this doesn't give the facing direction, you may need to add 90, 180, or 270 degrees to your angle, for example: math.rad(body.rotation+90) )
The above code will give you the x and y components of the unit vector in the direction of the rotation. You'll probably also need some multiplier to get the magnitude of force you want.
local forceMag = 0.5 -- change this value to apply more or less force
-- now apply the force
body:applyLinearImpulse(forceMag*xComp, forceMag*yComp, body.x, body.y)
Here's where I got the math: http://www.mathopenref.com/trigprobslantangle.html. Using a unit vector simplifies the math because the hypotenuse is always 1
How about making your own character moving towards an angle before using the confusing physics?
angle = math.rad(Insert the angle you want here)
character.x = character.x - math.sin(angle)
character.y = character.y + math.cos(angle)
Er. You don't need Trigonometry just to move the object.
Add
object:translate(distanceToMoveInXAxis,distanceToMoveInYAxis)
Or if you want to perform a transition,
transition.to(object,{x=object.x + distanceToMoveInXAxis,y=object.y + distanceToMoveInYAxis})

Resources