How to change the velocity in each frame in Love2D? - lua

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

Related

Gravity not working correctly

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.

Making object locked to a circle "follow" mouse

I'm making a circle pong game (where there's only one paddle and you move in a circle with the ball spawning in the middle of the circle)
Currently, I've almost everything down but I feel like using the keyboard to move the paddle is too slow and I cannot find any "middle" value where it's not too fast or slow
I saw some other examples of this game using the mouse to control the paddle but I have no idea how to do such a thing.
This is my update function for the paddle (sorry if the way I handle updating is ugly):
pad:update(
function(dt,self)
local mouseX,mouseY=love.mouse.getPosition()
self.rot=math.atan2((400 - self.x), -(300 - self.y))
--self.rot=math.atan2((mouseX - self.x), -(mouseY - self.y))
self.x = circleRadius*math.cos(self.r) + self.orgX;
self.y = circleRadius*math.sin(self.r) + self.orgY;
if love.keyboard.isDown("a") then
self.r=self.r+4*dt
end
if love.keyboard.isDown("d") then
self.r=self.r-4*dt
end
end,
dt
)
The above code is inside love.update and sends a function as an argument to pads update function, which then calls that function, giving it the correct arguments like self and dt.
r is basically the position of the paddle on the circle
Got it by setting the current position on the circle (r) to the angle between mouse and the centre of the circle (which in my case is the centre of the window which is 800x600)
self.r=math.atan2((400-mouseX), -(300-mouseY))+math.rad(90)

Crowd/zombie horde collision

So I'm trying to remake a flash game I made about a year ago but in love2D. I've got to the point where it's basically finished so I've decided to go and fix a few things.
The first thing was that I was using a normal AABB collision detection on rotated objects and sometimes bullets would go straight through zombies(I fixed this using a tutorial on 2D collision in roblox, go figure.)
The next thing was I wanted zombies to "push away" from eachother when they collided rather than merging into eachother.
The tutorial I used for rotated 2D collision also showed how to move objects away from eachother when they collided.
Each zombie is stored in a table, and then I loop through the tables to do stuff with them (like draw them, update etc. Zombies are created through another class)
But since they're in the same table, I'm not really sure how to go about making sure they're colliding with eachother.
for izom,zom in ipairs(main.zombies) do
zom:update(dt)
doesCollide,mtv = collide(zom,zom)
if collide(zom,zom) then
zom.Position = zom.Position + Vector2.new(mtv.x, mtv.y);;
end
end
I tried that, but it doesn't work.
How can I fix this?
I would go with a nested for loop like Dimitry said.
As far as pushing away goes I would check if the distance between both zombies center points is less than a full width of a zombie, and if so push away.
It would look something like this
local pushBack=50 --How hard they should push away from eachother
--Loop through the zombies table twice
for ia,zombiea in ipairs(main.zombies) do
for ib,zombieb in ipairs(main.zombies) do
--Get the distance between the two zombies
local dx = zombiea.x - zombieb.x
local dy = zombiea.y - zombieb.y
distance = math.sqrt ( dx * dx + dy * dy )
--If the zombies are touching
if distance<zombiea.width then
local angle=math.atan2((zombieb.y-zombiea.y),(zombieb.x-zombiea.x))
--Push zombie A
zombiea.x=zombiea.x+math.cos(angle)*pushBack*dt
zombiea.y=zombiea.y+math.sin(angle)*pushBack*dt
--Push zombie B
zombiea.x=zombiea.x+math.cos(angle)*pushBack*dt
zombiea.y=zombiea.y+math.sin(angle)*pushBack*dt
end
end
end
You may need to add a + math.pi to the final distance calculation to get the right directions.
distance = math.sqrt ( dx * dx + dy * dy ) + math.pi
Also you would probably want the pushBack variable to change dynamically depending on how close they are to give it a smoother feel.
Also I don't know how you are handling the movement of the zombies, but hopefully this will still help!

To set accelerometer for corona game

I am creating a game which uses accelerometer to move ball (football model game).
function acc(e)
physics.setGravity(e.xInstant*(screenW/4), -1*e.yInstant*(screenH/4))
end
But this code not giving a smooth flow of game. Can u guys help me for right option
You either need to "calibrate" or to change the algorithm. I would try calibrate first: replace your acc() with
function acc(e)
local calibX = 1
local calibY = 1
-- physics.setGravity(calibX * e.xInstant*(screenW/4), -calibY * e.yInstant*(screenH/4))
print(e.xInstant, e.yInstant, e.zInstant)
end
and do the tilting that you feel is "not smooth", looking at the values printed when you do that. This will tell you what calibX and Y should be, set them then uncomment the physics line, comment out the print line, try again, until you get it right. For example, if the x and y instant are around 10, you could try calibX = 0.1 or less.
If you can't find values for calibration coefficients that give you desired motion, you need to change your algorithm. For example, if you are trying to simulate the tilt of your device as though it was a table on which a marble rolls, and tilting the table should make marble move, then changing gravity is not the way to do it. You want to apply a horizontal force which is equal to g*sin(theta) where g is gravity and theta is the tilt angle of the device side-to-side. The formula is slightly more complex if you allow tilt along the other direction.

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.

Resources