Lua raycasting issues - lua

I have worked day-in and day-out on a ray-casting engine I'm building for the Ti Nspire CX (with Lua), and am having issues with ray collision.
I have fiddled around with the area which I believe has the problem, because drawing of rays on the screen has no issues:
I have also done ALOT of debugging in this, such as displaying the coordinates which the rays are located when being shot outwards from the player. I say that I believe that the collision part has the problem because when I printed the radius of each ray, they all reached the maximum distance, which I set to be 40. This is the collision and single-ray management code:
function ray()
radius = 1
for n = 1, 40, 1 do -- increase of testdot
x_ray = x + (radius * math.cos(rayf * 3.141592653/180))
y_ray = y - (radius * math.sin(rayf * 3.141592653/180))
--print(math.floor(x_ray,3), math.floor(y_ray,3), rayf, radius)
for i = 1, 4, 1 do --for k,v in pairs(map) do -- testing for collision of testdot and a wall
--print("X ",v[1],"<-->",math.floor(x_ray),"<-->",v[3])
--print("Y ",v[2],"<-->",math.floor(y_ray),"<-->",v[4])'
------------------------------------
if (
math.min(map[i][1],map[i][3]) <= x_ray and x_ray <= math.max(map[i][1],map[i][3])
) and (
math.min(map[i][2],map[i][4]) <= y_ray and y_ray <= math.max(map[i][2],map[i][4])
) then
print("Collision")
--return true
end
------------------------------------
end
radius = n
end
end
I know the second for-loop could be condensed, but I did this in my debugging process to find out why this won't function as it should.
The area around the ------------------------------------ is where the rays don't collide/over-reach/miss... I dont know why this isn't working, anyone have any suggestions?
Just for reference, this collision is based off a python program I was having issues with here, in, of course, the collision part.
Values of variables:
x, y is the position of the player (while raycasting this will remain static)
radius is the current radius of a single ray, and will continue to increment as long as no collision is detected
rayf is the current degree of the ray (is not relative to the player). Is calculated at the start of the program by taking the players degree (which isnt shown in here but is called 'facing'), adding 30, then rotating clockwise until the FOV of 60 degrees has been satisfied.
X-ray, y_ray are the current points of a single ray, and will continue to increment towards the specified rayf value, and will increment in values of 1 to make a radius equal to the n in the last for-loop. (Must note that the degrees are the same in a typical unit circle, and are NOT mirrored to match this mirrored y-axis; i.e. 90 degrees is up, 180 degrees is down.)

This isn't code review site but I'm going to try to first write the code in more understandable manner and then to guess the error in comments to the code.
function ray(x,y,rayf,map)
%Are you sure that your global variables x,y,rayf are not overwritten?
%I believe these are correct if 0 degrees is "right", "90" - top and "180" - left
%is it so?
local x_proj = math.cos(rayf* 3.141592653/180);
local y_proj = -math.sin(rayf* 3.141592653/180);
for radius=1,40 do
local x_ray = x + radius * x_proj
local y_ray = y + radius * y_proj
for i=1,4 do
%I take it the map[i] is a particular rectangle located at a particular side of the room (so map[1] is located at the left edge of the screen, for example)
%is it so?
local wall_left_edge = math.min ( map[i][1],map[i][3] )
local wall_right_edge = math.max ( map[i][1],map[i][3] )
%if I understood correctly, the smaller y is above bigger y
local wall_top_edge = math.min ( map[i][2], map[i][4] )
local wall_bottom_edge = math.max ( map[i][2], map[i][4] )
%it is beyond me why couldnt you just sort the wall coordinates beforehand
%(say, top - 1 , bottom - 2 left - 3, right - 4)
if (wall_left_edge < x) and (x < wall_right_edge)
and (wall_top_edge < y) and (y < wall_bottom_edge) then
%this does not detect collision,
%it detects whether beam steps into the rectangle "map[i]"
print("Collision")
end
end
end
end
So, with taking into account the last comment, the walls you define must be broad and thick enough so that the beam is guaranteed to step into one: (wall_right_edge - wall_left_edge ) > 1 ( 1 is step of the radius loop) and (wall_bottom_edge - wall_top_edge ) > 1. At the corners walls must either overlap or they should share a boundary of length at least 1.

The reason the rays were always going past 40 was because the for-loop wasnt being canceled, which is a great reason to include a return to break out of the function and continue the code (I thought a return was included, however it wasn't functioning correctly, as you see in the:
--return true
Past this, the raycasting worked fine, but not math.floor-ing the new ray coord's also made the ray shoot beyond 40, for some unknown reason.

Related

Delta time and understimate( Im not understanding the delta time)

Hey you guys im with problems of understanding these problems with delta time. Its in Pong-3 of course CS50 - Introduction to Game devlopment. Im not understanding why dt is there. dt is a variable not mentioned in all code. BUt is callled in render process in language LUA. Its like a local variable but is not pre-defined or another way of introduce that variable. I will give the code and wait for... some answer.
--[[
GD50 2018
Pong Remake
pong-3
"The Paddle Update"
-- Main Program --
Author: Colton Ogden
cogden#cs50.harvard.edu
Originally programmed by Atari in 1972. Features two
paddles, controlled by players, with the goal of getting
the ball past your opponent's edge. First to 10 points wins.
This version is built to more closely resemble the NES than
the original Pong machines or the Atari 2600 in terms of
resolution, though in widescreen (16:9) so it looks nicer on
modern systems.
]]
-- push is a library that will allow us to draw our game at a virtual
-- resolution, instead of however large our window is; used to provide
-- a more retro aesthetic
--
-- https://github.com/Ulydev/push
push = require 'push'
WINDOW_WIDTH = 1280
WINDOW_HEIGHT = 720
VIRTUAL_WIDTH = 432
VIRTUAL_HEIGHT = 243
-- speed at which we will move our paddle; multiplied by dt in update
PADDLE_SPEED = 200
--[[
Runs when the game first starts up, only once; used to initialize the game.
]]
function love.load()
love.graphics.setDefaultFilter('nearest', 'nearest')
-- more "retro-looking" font object we can use for any text
smallFont = love.graphics.newFont('font.ttf', 8)
-- larger font for drawing the score on the screen
scoreFont = love.graphics.newFont('font.ttf', 32)
-- set LÖVE2D's active font to the smallFont obect
love.graphics.setFont(smallFont)
-- initialize window with virtual resolution
push:setupScreen(VIRTUAL_WIDTH, VIRTUAL_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT, {
fullscreen = false,
resizable = false,
vsync = true
})
-- initialize score variables, used for rendering on the screen and keeping
-- track of the winner
player1Score = 0
player2Score = 0
-- paddle positions on the Y axis (they can only move up or down)
player1Y = 30
player2Y = VIRTUAL_HEIGHT - 50
end
--[[
Runs every frame, with "dt" passed in, our delta in seconds
since the last frame, which LÖVE2D supplies us.
]]
function love.update(dt)
-- player 1 movement
if love.keyboard.isDown('w') then
-- add negative paddle speed to current Y scaled by deltaTime
player1Y = player1Y + -PADDLE_SPEED * dt
elseif love.keyboard.isDown('s') then
-- add positive paddle speed to current Y scaled by deltaTime
player1Y = player1Y + PADDLE_SPEED * dt
end
-- player 2 movement
if love.keyboard.isDown('up') then
-- add negative paddle speed to current Y scaled by deltaTime
player2Y = player2Y + -PADDLE_SPEED * dt
elseif love.keyboard.isDown('down') then
-- add positive paddle speed to current Y scaled by deltaTime
player2Y = player2Y + PADDLE_SPEED * dt
end
end
--[[
Keyboard handling, called by LÖVE2D each frame;
passes in the key we pressed so we can access.
]]
function love.keypressed(key)
-- keys can be accessed by string name
if key == 'escape' then
-- function LÖVE gives us to terminate application
love.event.quit()
end
end
--[[
Called after update by LÖVE2D, used to draw anything to the screen,
updated or otherwise.
]]
function love.draw()
-- begin rendering at virtual resolution
push:apply('start')
-- clear the screen with a specific color; in this case, a color similar
-- to some versions of the original Pong
love.graphics.clear(40/255, 45/255, 52/255, 255/255)
-- draw welcome text toward the top of the screen
love.graphics.setFont(smallFont)
love.graphics.printf('Hello Pong!', 0, 20, VIRTUAL_WIDTH, 'center')
-- draw score on the left and right center of the screen
-- need to switch font to draw before actually printing
love.graphics.setFont(scoreFont)
love.graphics.print(tostring(player1Score), VIRTUAL_WIDTH / 2 - 50,
VIRTUAL_HEIGHT / 3)
love.graphics.print(tostring(player2Score), VIRTUAL_WIDTH / 2 + 30,
VIRTUAL_HEIGHT / 3)
-- render first paddle (left side), now using the players' Y variable
love.graphics.rectangle('fill', 10, player1Y, 5, 20)
-- render second paddle (right side)
love.graphics.rectangle('fill', VIRTUAL_WIDTH - 10, player2Y, 5, 20)
-- render ball (center)
love.graphics.rectangle('fill', VIRTUAL_WIDTH / 2 - 2, VIRTUAL_HEIGHT / 2 - 2, 4, 4)
-- end rendering at virtual resolution
push:apply('end')
end
The Love update docs mention delta time briefly.
dt (Delta Time) holds the time in seconds since the last time the update function was called. Under normal circumstances this should be a small fraction of a second. The Love framework calls the update function for you repeatedly as part of your game loop. It also defines and keeps track of the dt variable somewhere within the framework, which is why you don't see it defined or introduced anywhere. You only have access to it from within the update function (unless you pass it elsewhere).
Why would you need the delta time?
Because you can't guarantee that update will always get called at a consistent rate. Imagine you have a player character that you move 1 pixel every time update is called. If your game is running at 60fps then your character moves 60px in 1 second. But of your game drops down to 59, or 50, or 12fps, then your character will slow down too, which is not usually what you want.
You can use the delta time to keep a consistent speed for your character. Multiplying the desired speed per second (60px) by dt will get you the exact amount to move each frame regardless of how often update is getting called.
That's what's happening in your code in places like this:
player2Y = player2Y + PADDLE_SPEED * dt
PADDLE_SPEED is the amount to move per second, multiplied by the number of seconds since the last update. The result is added to the player's position.

Trig, placing items and updating their position

I have a problem I just cannot solve, and after a week it's really winding me up.
Background.
I'm placing items onto a circle using basic trig. The number of items can change dynamically, and they are spaced around the circle equally.
The items rotate around the circle, and the speed of rotation changes to be in sync with a BPM (Beats Per Minute) clock. This clock can change at any time.
The problem I'm having is that the items seem to be placed randomly on the circle, not equally spaced in order (see image 1). They'll appear out of order even though it's a basic for loop that places them. I think they may in face be in order, but the rotation values may be off making them look like they are in an odd order.
The second issue is that when the number of items reduces, the speed of rotation increases (it shouldn't) and if the number increases, the speed slows.
So I expect an issue with my trig function. I'm showing the complete code here but can simplify if it'll help.
What have I tried?
I've tried simplified versions without the graphical output, and the numbers all seem to make perfect sense. The radians between items is correct, the placement looks correct. It all looks correct, but it isn't.
The code.
--the variables
orbitalCircle.xPos = x or 0
orbitalCircle.yPos = y or 0
orbitalCircle.circleDiameter = diameter or 10
orbitalCircle.numberOfNotes = number_of_notes
orbitalCircle.spaceBetweenNotes = (360 / number_of_notes)
orbitalCircle.beatsPerSecond = (beats_per_minute / 60)
orbitalCircle.currentRotation = 0
orbitalCircle.framesPerSecond = frames_per_second or 15
orbitalCircle.framesPerFullRotation = (orbitalCircle.numberOfNotes/orbitalCircle.beatsPerSecond)+orbitalCircle.framesPerSecond
orbitalCircle.degreesPerFrame = 360 / orbitalCircle.framesPerFullRotation
orbitalCircle.newRotationValue = orbitalCircle.currentRotation + orbitalCircle.degreesPerFrame
orbitalCircle.sequenceData = sequence_data
--the function that updates the sequence data and therefore the number of items on the circle
function orbitalCircle.updateNotes(sq)
orbitalCircle.sequenceData = sq
orbitalCircle.numberOfNotes = (#sq)
orbitalCircle.spaceBetweenNotes = (360 / orbitalCircle.numberOfNotes)
end
--the function that calculates the new rotation value of the item to be placed on the circle
function orbitalCircle.tick()
orbitalCircle.spaceBetweenNotes = (360 / number_of_notes)
orbitalCircle.framesPerFullRotation = (orbitalCircle.numberOfNotes/orbitalCircle.beatsPerSecond)*orbitalCircle.framesPerSecond
orbitalCircle.degreesPerFrame = (360 / orbitalCircle.framesPerFullRotation)
orbitalCircle.newRotationValue = (orbitalCircle.currentRotation + orbitalCircle.degreesPerFrame)
if orbitalCircle.newRotationValue > 360 then
orbitalCircle.currentRotation = 0
else
orbitalCircle.currentRotation = orbitalCircle.newRotationValue
end
end
--finally the function that places the items onto the circle
function orbitalCircle.redraw()
screen.circle(orbitalCircle.xPos, orbitalCircle.yPos, orbitalCircle.circleDiameter)
screen.stroke()
for i=1, (#orbitalCircle.sequenceData) do
if orbitalCircle.sequenceData[i] > 0 then
screen.circle(
math.cos(math.rad(orbitalCircle.newRotationValue)+(orbitalCircle.spaceBetweenNotes*i))*orbitalCircle.circleDiameter + orbitalCircle.xPos,
math.sin(math.rad(orbitalCircle.newRotationValue)+(orbitalCircle.spaceBetweenNotes*i))*orbitalCircle.circleDiameter + orbitalCircle.yPos,
map(orbitalCircle.sequenceData[i], 5, 128, 0.5, 4)
)
end
end
end
end
I'd expect that the items would be:
equally spaced no matter the amount (that works)
in order (they appear not to be)
the speed of rotation should remain fixed unless the BPM changes (this doesn't happen)
I'm lost!
Let us take a closer look at the drawing.
screen.circle(
math.cos(math.rad(orbitalCircle.newRotationValue)+(orbitalCircle.spaceBetweenNotes*i))*orbitalCircle.circleDiameter + orbitalCircle.xPos,
math.sin(math.rad(orbitalCircle.newRotationValue)+(orbitalCircle.spaceBetweenNotes*i))*orbitalCircle.circleDiameter + orbitalCircle.yPos,
map(orbitalCircle.sequenceData[i], 5, 128, 0.5, 4)
)
What is the angle that is being drawn here? It is the argument to math.cos and math.sin (I will ignore the scaling and the translation that is applied afterwards):
math.rad(orbitalCircle.newRotationValue)+(orbitalCircle.spaceBetweenNotes*i)
So... it is the neRotationValue converted to radians and added to that the space between notes. This one is defined as 360 / number_of_notes, so it is in degrees. Adding a radians and degrees most likely does not produce the expected result.
So, what exactly do you mean with the following?
I've tried simplified versions without the graphical output, and the numbers all seem to make perfect sense.

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.

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})

How to create Random Geo-Points within a distance d from another Geo-point?

How to get Random Geo-points[ lat/long in decimal], placed anywhere inside a 100 meter radius circle? The center of the circle is another reference GeoPoint.
Is there any Function/Formulae that implements this?
Basically I am reading the GPS input of my android device and need to generate random Geo-Points around the device [In a circle of radius 100 meters centered at my device].
Please note : There are no Geo-Points pre-stored in Database. I need to create all the Geo-points on the fly as mentioned above.
I just wrote a a Ruby method which extends Random to provide this.
Caveat: The points all lay within a box, not a circle.
class Random
def location(lat, lng, max_dist_meters)
This is called with Random.new.location(mid_lat, mid_lng, dist). It will return a point which is probably within max_dist_meters of a the mid point.
max_radius = Math.sqrt((max_dist_meters ** 2) / 2.0)
Without adjusting to max_radius we'd get points inside a square outside the circle (i.e. in the corners), where the distance would be greater than max_dist_meters. This constrains us to a square inside the circle which is probably more what you want.
lat_offset = rand(10 ** (Math.log10(max_radius / 1.11)-5))
lng_offset = rand(10 ** (Math.log10(max_radius / 1.11)-5))
The 1.11 and 5 come from here.
lat += [1,-1].sample * lat_offset
lng += [1,-1].sample * lng_offset
lat = [[-90, lat].max, 90].min
lng = [[-180, lng].max, 180].min
We should probably wrap around here instead of just clamping the value, fixes welcome.
[lat, lng]
end
end
Comments / clean up welcome!
Sample output here which you can see nicely if you paste the lat/lngs here.
Pick random points on a square (i.e. pairs of uniform random numbers), then discard any that don't lie within a circle inscribed in that square. Given (x,y) pairs, a point is within your circle if:
(x - c_x)^2 + (y - c_y)^2 < r,
where (c_x, c_y) is the centre of your circle and r is its radius.
Start here: Generate a random point within a circle (uniformly). Then figure out how to center that circle at the reference lat/long. Then figure out how to map the randomly generated points to lat/long values. Hint: you want to add the individual components (say, x and y on your circle) to the circle's center.

Resources