This is a little library I was making for the LOVE2D engine in Lua, which uses separating axis theorem to solve collisions.
I was so happy when I got my SAT program to work, and started testing it with a multitude of polygons. It works in most cases, and gives a correct minimum translation vector for them too. Oddly enough- if both shapes have acute angles, then those angles cause the program to fail, returning collisions when the shape isn't touching, or even more unusually, it gives a bizarre minimum translation vector. I have checked my function that returns normals- as I felt that this was my first point that could have failed, but it seems to be working fine.
This is the main function that handles my collision.
function findIntersection(shape1, shape2)
--Get axes to test.
--MTV means 'minimum translation vector' ie. the shortest vector of intersection
local axes1 = {}
local axes2 = {}
local overlap = false
local MTV = {direction = 0, magnitude = 99999}
for i, vert in pairs(shape1.hitbox) do
nrm = getNormal(shape1.hitbox, i)
table.insert(axes1, nrm)
end
for i, vert in pairs(shape2.hitbox)do
nrm = getNormal(shape2.hitbox, i)
table.insert(axes2, nrm)
end
--print(#axes1 .. ' ' .. #axes2)
--now that we have the axes, we have to project along each of them
for i, axis in pairs(axes1) do
test1 = hitboxProj(shape1, vectorToCoord(axis.direction, axis.magnitude))
test2 = hitboxProj(shape2, vectorToCoord(axis.direction, axis.magnitude))
if test2.max > test1.min or test1.max > test2.min then
if test2.max - test1.min < MTV.magnitude then
MTV.direction = axes1[i].direction
MTV.magnitude = test2.max - test1.min
end
else
return false
end
end
--now that we have the axes, we have to project along each of them
for i, axis in pairs(axes2) do
test1 = hitboxProj(shape1, vectorToCoord(axis.direction, axis.magnitude))
test2 = hitboxProj(shape2, vectorToCoord(axis.direction, axis.magnitude))
if test2.max > test1.min or test1.max > test2.min then
if test2.max - test1.min < MTV.magnitude then
MTV.direction = axes2[i].direction
MTV.magnitude = test2.max - test1.min
end
else
return false
end
end
return {MTV}
end
My project files are here on github https://github.com/ToffeeGoat/ToffeeCollision
It's a good start and your code is fairly clear. There are some things which can be improved, in particular I see that all of your functions are global. For starters you want to store all of your functions in a "module" to avoid polluting the _G space. You can use locals for everything else.
Note that it's not robust to write things like x == 0 this check will only work for integers and may fail when floating point math is involved. I recommend writing a simple test script for each function in your library.
Also, it's not efficient to write return {x = xCoord, y = yCoord} when you can return multiple values with Lua return xCoord, yCoord. Creating a lot of intermediate tables puts a strain on the garbage collector.
Some of your code needs reworking like the "getDirection" function. I mean there are already well-known techniques for this sort of thing. Check out my tutorial for examples: https://2dengine.com/?p=vectors#Angle_between_two_vectors
There is some silly stuff in there like function sq(x) return x*x end. Did you know you can write x^2 in Lua?
addDeg can be replaced by the modulo operator: newAng = (angle + addition)%360
Also note that there is absolutely no benefit to working with degrees - I recommend using only radians. You are already using math.pi which is in radians. Either way you have to pick either radians or degrees and stick to one or the other. Don't use both units in your code.
I don't want to nitpick too much because your code is not bad, you just need to get used to some of the best practices. Here is another one of my tutorials:
https://2dengine.com/?p=intersections
Related
I don't know what the issue is exactly but I believe the heuristic function is doing something funky.
Breadth search works 100% normally.
But when I add in the heuristic function for priority it just suddenly does really weird stuff, resulting in completely strange paths.
Not just that but it doesn't even finish making the pathing grids (came-from, cost-so-far-and-such), stopping just before the ending.
Then when I try to add the A* part and it's still weird.
At first I didn't mind but after fiddling it just doesn't make sense, I've tried moving things around and trying to figure out if it's something to do with when it's inserted to the table or if it's the Manhattan distance heuristic itself but I've done everything as correctly as I can think to.
Here's my update code:
function updategraph(graph, frontier, camefrom, costsofar, goal)
-- continue while there are entries in the frontier
if #frontier > 0 then
local pos = frontier[1]
-- go over all neighbours
for i, p in ipairs(getcons(graph, pos)) do
local oldcost = costsofar[pos[1]] and costsofar[pos[1]][pos[2]] or 0
local newcost = (graph[p[1]][p[2]].cost or 1) + oldcost
local hascsf = (costsofar[p[1]] and costsofar[p[1]][p[2]])
local solid = (graph[p[1]] and graph[p[1]][p[2]] and graph[p[1]][p[2]].solid)
if not solid and
(not hascsf
or (hascsf and newcost < costsofar[pos[1]][pos[2]]))
then
if not camefrom[p[1]] then camefrom[p[1]] = {} end
camefrom[p[1]][p[2]] = pos
if not costsofar[p[1]] then costsofar[p[1]] = {} end
costsofar[p[1]][p[2]] = newcost
-- use heuristic to find the priority
local priority = math.max(1, math.min(#frontier, newcost + heuristic(goal, p)))
table.insert(frontier, priority, p)
-- if the current position is the goal
if goal[1] == p[1] and goal[2] == p[2] then
return false
end
end
end
-- remove from the frontier
table.remove(frontier, 1)
end
end
The heuristic:
function heuristic(goal, nxt)
-- manhattan distance on a square grid
return math.abs(goal[1] - nxt[1]) + math.abs(goal[2] - nxt[2])
end
Code for getting neighbours/connections:
function getcons(graph, pos)
-- acquire neighbours
local r = {}
-- make sure the grid space exists before adding it to the neighbours list
if graph[pos[1]+1] and graph[pos[1]+1][pos[2]] then
table.insert(r, {pos[1]+1, pos[2]})
end
if graph[pos[1]-1] and graph[pos[1]-1][pos[2]] then
table.insert(r, {pos[1]-1, pos[2]})
end
if graph[pos[1]] and graph[pos[1]][pos[2]+1] then
table.insert(r, {pos[1], pos[2]+1})
end
if graph[pos[1]] and graph[pos[1]][pos[2]-1] then
table.insert(r, {pos[1], pos[2]-1})
end
return r
end
Apologies if I've asked anything incorrectly, tell me and I'll give any more needed information.
I have a problem where I need to do a linear interpolation on some data as it is acquired from a sensor (it's technically position data, but the nature of the data doesn't really matter). I'm doing this now in matlab, but since I will eventually migrate this code to other languages, I want to keep the code as simple as possible and not use any complicated matlab-specific/built-in functions.
My implementation initially seems OK, but when checking my work against matlab's built-in interp1 function, it seems my implementation isn't perfect, and I have no idea why. Below is the code I'm using on a dataset already fully collected, but as I loop through the data, I act as if I only have the current sample and the previous sample, which mirrors the problem I will eventually face.
%make some dummy data
np = 109; %number of data points for x and y
x_data = linspace(3,98,np) + (normrnd(0.4,0.2,[1,np]));
y_data = normrnd(2.5, 1.5, [1,np]);
%define the query points the data will be interpolated over
qp = [1:100];
kk=2; %indexes through the data
cc = 1; %indexes through the query points
qpi = qp(cc); %qpi is the current query point in the loop
y_interp = qp*nan; %this will hold our solution
while kk<=length(x_data)
kk = kk+1; %update the data counter
%perform online interpolation
if cc<length(qp)-1
if qpi>=y_data(kk-1) %the query point, of course, has to be in-between the current value and the next value of x_data
y_interp(cc) = myInterp(x_data(kk-1), x_data(kk), y_data(kk-1), y_data(kk), qpi);
end
if qpi>x_data(kk), %if the current query point is already larger than the current sample, update the sample
kk = kk+1;
else %otherwise, update the query point to ensure its in between the samples for the next iteration
cc = cc + 1;
qpi = qp(cc);
%It is possible that if the change in x_data is greater than the resolution of the query
%points, an update like the above wont work. In this case, we must lag the data
if qpi<x_data(kk),
kk=kk-1;
end
end
end
end
%get the correct interpolation
y_interp_correct = interp1(x_data, y_data, qp);
%plot both solutions to show the difference
figure;
plot(y_interp,'displayname','manual-solution'); hold on;
plot(y_interp_correct,'k--','displayname','matlab solution');
leg1 = legend('show');
set(leg1,'Location','Best');
ylabel('interpolated points');
xlabel('query points');
Note that the "myInterp" function is as follows:
function yi = myInterp(x1, x2, y1, y2, qp)
%linearly interpolate the function value y(x) over the query point qp
yi = y1 + (qp-x1) * ( (y2-y1)/(x2-x1) );
end
And here is the plot showing that my implementation isn't correct :-(
Can anyone help me find where the mistake is? And why? I suspect it has something to do with ensuring that the query point is in-between the previous and current x-samples, but I'm not sure.
The problem in your code is that you at times call myInterp with a value of qpi that is outside of the bounds x_data(kk-1) and x_data(kk). This leads to invalid extrapolation results.
Your logic of looping over kk rather than cc is very confusing to me. I would write a simple for loop over cc, which are the points at which you want to interpolate. For each of these points, advance kk, if necessary, such that qp(cc) is in between x_data(kk) and x_data(kk+1) (you can use kk-1 and kk instead if you prefer, just initialize kk=2 to ensure that kk-1 exists, I just find starting at kk=1 more intuitive).
To simplify the logic here, I'm limiting the values in qp to be inside the limits of x_data, so that we don't need to test to ensure that x_data(kk+1) exists, nor that x_data(1)<pq(cc). You can add those tests in if you wish.
Here's my code:
qp = [ceil(x_data(1)+0.1):floor(x_data(end)-0.1)];
y_interp = qp*nan; % this will hold our solution
kk=1; % indexes through the data
for cc=1:numel(qp)
% advance kk to where we can interpolate
% (this loop is guaranteed to not index out of bounds because x_data(end)>qp(end),
% but needs to be adjusted if this is not ensured prior to the loop)
while x_data(kk+1) < qp(cc)
kk = kk + 1;
end
% perform online interpolation
y_interp(cc) = myInterp(x_data(kk), x_data(kk+1), y_data(kk), y_data(kk+1), qp(cc));
end
As you can see, the logic is a lot simpler this way. The result is identical to y_interp_correct. The inner while x_data... loop serves the same purpose as your outer while loop, and would be the place where you read your data from wherever it's coming from.
I am making a roguelike in Love2D as a hobby project. My approach is to try and use as much of the native capabilities of Lua and the Love2D (0.10.1) API as possible, without relying on fancy libraries like middleclass or HUMP, so as to learn more about the language.
After reading PiL's chapters on OOP and seeing the power there, I decided to set up a Mob class (using metamethods to emulate class functionality) that encompasses the players, monsters, and other NPCs (anything that can move). So, far, it's working beautifully, I can create all kinds of instances easily that share methods and all that stuff. But there's a lot of things I don't know how to do, yet, and one of them is holding my prototype up from further progress.
Setting up collision with the map itself wasn't too bad. My maps are tables full of tables full of integers, with 0 being the floor. The game draws "." and "#" and "+" and such to denote various inanimate objects, from each table. Player 1 moves using the numpad, and their position is tracked by dividing their raw pixel position by 32 to create a grid of 32x32 "tiles". Then, inside love.keypressed(key), I have lines like:
if key == "kp8" and currentmap[player1.grid_y - 1][player1.grid_x] == 0 then
player1.grid_y = player1.grid_y - 1
and so on, with elseifs for each key the player can press. This prevents them from walking through anything that isn't an open floor tile in the map itself.
But, I'm trying to implement some kind of "collision detection" to prevent MOBs from walking through each other and to use in writing the rules for combat, and this is trickier. I had a method in place to calculate the distance between mobs, but I'm told this might eventually cause rounding errors, plus it had to be written for each combination of mobs I want to test, individually.
What I'd like to know is: Is there a known (preferably elegant) way to get all instances of a particular class to pass some number of values to a table?
What I'd like to do is "ask" every Mob on a given map where they are, and have them "report" self.grid_x and self.grid_y to another layer of map that's just for tracking mobs (1 if self.is_here is true, 0 if not, or similar), that gets updated every turn. Then, I could implement collision rules based on coordinates being equal, or maybe a foo.is_here flag or something.
I have only vague ideas about how to proceed, however. Any help would be appreciated, including (and maybe especially) feedback as to a better way to do what I'm trying to do. Thanks!
A simple idea is to store "who is here" information for every cell of the field and update this information on every move of every object.
function create_game_field()
-- initialize a table for storing "who is here" information
who_is_here = {}
for y = 1,24 do
who_is_here[y] = {}
for x = 1,38 do
who_is_here[y][x] = 0
end
end
end
function Mob:can_move(dx, dy)
local u = currentmap[self.y + dy][self.x + dx]
local v = who_is_here[self.y + dy][self.x + dx]
if u == 0 and v == 0 then
return true
else
end
end
function Mob:move(dx, dy)
-- update "who is here"
who_is_here[self.y][self.x] = 0
self.x, self.y = self.x + dx, self.y + dy
who_is_here[self.y][self.x] = 1
end
function Mob:who_is_there(dx, dy) -- look who is standing on adjacent cell
return who_is_here[self.y + dy][self.x + dx] -- return mob or nil
end
function Mob:roll_call()
who_is_here[self.y][self.x] = 1
end
Usage example:
-- player1 spawns in at (6,9) on the grid coords
player1 = Mob:spawn(6,9)
-- player1 added to who_is_here
player1:roll_call()
Then, in love.keypressed(key):
if key == "kp8" and player1:can_move(0, -1) then
player1:move(0, -1)
end
There are a few ways you could get all your instances data but one of the simpler ones is probably to have them all be added to a table when they are created. Providing you add the entire table for that instance, all the values will update in the main table because it acts like a collection of pointers.
function mob:new( x, y, type )
self.x = 100
self.y = 200
self.type = type
-- any other declarations you need
table.insert(allMobs, self)
return self
end
Here we insert all the mobs into the table 'allMobs'. Once we have that we can simply iterate through and get all our coordinates.
for i, v in ipairs(allMobs) do
local x, y = v.x, v.y
-- Do whatever you need with the coordinates. Add them to another table, compare
-- them to others, etc.
end
Now we have a table with all our mobs in it and a way to access each of their positions. If you have any further inquiries then let me know.
I would like to evaluate a math string in my corona app. Right now I'm focusing on the trig functions, so let's let the example be the most difficult we're likely to face:
local expr = "2sin(4pi+2)+7"
My goal is for this to somehow be (either) evaluated as is with maybe a pi --> math.pi switch, or to even break it up. The breaking up would be much more difficult, however, since it COULD be as complicated a above, but could also just be sin(1).
So I would prefer to stay as close to the python eval(expr) function as possible, but if that can't happen, I am flexible.
The simplest way would be to replace sin with math.sin (pi with math.pi and so on), add missing multiplications signs, and run it through loadstring, but loadstring is not available in Corona environment.
This means you will need to write your own parser for these expressions. I found a discussion on Corona forums that may help you as a starting point: here, with some details and a demo here
This should do the trick, it is able to use the lua math functions without putting 'math.function' so just sqrt(100) works fine. I threw this together because I have seen this question asked way too many times. Hopes this helps :)
If you have any questions feel free to contact me at rayaman99#gmail.com
function evaluate(cmd,v) -- this uses recursion to solve math equations
--[[ We break it into pieces and solve tiny pieces at a time then put them back together
Example of whats going on
Lets say we have "5+5+5+5+5"
First we get this:
5+5+5+5 + 5
5+5+5 + 5
5+5 + 5
5 + 5
Take all the single 5's and do their opperation which is addition in this case and get 25 as our answer
if you want to visually see this with a custom expression, uncomment the code below that says '--print(l,o,r)'
]]
v=v or 0
local count=0
local function helper(o,v,r)-- a local helper function to speed things up and keep the code smaller
if type(v)=="string" then
if v:find("%D") then
v=tonumber(math[v]) or tonumber(_G[v]) -- This section allows global variables and variables from math to be used feel free to add your own enviroments
end
end
if type(r)=="string" then
if r:find("%D") then
r=tonumber(math[r]) or tonumber(_G[r]) -- A mirror from above but this affects the other side of the equation
-- Think about it as '5+a' and 'a+5' This mirror allows me to tackle both sides of the expression
end
end
local r=tonumber(r) or 0
if o=="+" then -- where we handle different math opperators
return r+v
elseif o=="-" then
return r-v
elseif o=="/" then
return r/v
elseif o=="*" then
return r*v
elseif o=="^" then
return r^v
end
end
for i,v in pairs(math) do
cmd=cmd:gsub(i.."(%b())",function(a)
a=a:sub(2,-2)
if a:sub(1,1)=="-" then
a="0"..a
end
return v(evaluate(a))
end)
end
cmd=cmd:gsub("%b()",function(a)
return evaluate(a:sub(2,-2))
end)
for l,o,r in cmd:gmatch("(.*)([%+%^%-%*/])(.*)") do -- iteration this breaks the expression into managable parts, when adding pieces into
--print(":",l,o,r) -- uncomment this to see how it does its thing
count=count+1 -- keep track for certain conditions
if l:find("[%+%^%-%*/]") then -- if I find that the lefthand side of the expression contains lets keep breaking it apart
v=helper(o,r,evaluate(l,v))-- evaluate again and do the helper function
else
if count==1 then
v=helper(o,r,l) -- Case where an expression contains one mathematical opperator
end
end
end
if count==0 then return (tonumber(cmd) or tonumber(math[cmd]) or tonumber(_G[cmd])) end
-- you can add your own enviroments as well... I use math and _G
return v
end
a=5
print(evaluate("2+2+2*2")) -- This still has work when it comes to pemdas; however, the use parentheses can order things!
print(evaluate("2+2+(2*2)"))-- <-- As seen here
print(evaluate("sqrt(100)"))
print(evaluate("sqrt(100)+abs(-100)"))
print(evaluate("sqrt(100+44)"))
print(evaluate("sqrt(100+44)/2"))
print(evaluate("5^2"))
print(evaluate("a")) -- that we stored above
print(evaluate("pi")) -- math.pi
print(evaluate("pi*2")) -- math.pi
I'm writing a LSL to Lua translator, and I'm having all sorts of trouble implementing incrementing and decrementing operators. LSL has such things using the usual C like syntax (x++, x--, ++x, --x), but Lua does not. Just to avoid massive amounts of typing, I refer to these sorts of operators as "crements". In the below code, I'll use "..." to represent other parts of the expression.
... x += 1 ...
Wont work, coz Lua only has simple assignment.
... x = x + 1 ...
Wont work coz that's a statement, and Lua can't use statements in expressions. LSL can use crements in expressions.
function preIncrement(x) x = x + 1; return x; end
... preIncrement(x) ...
While it does provide the correct value in the expression, Lua is pass by value for numbers, so the original variable is not changed. If I could get this to actually change the variable, then all is good. Messing with the environment might not be such a good idea, dunno what scope x is. I think I'll investigate that next. The translator could output scope details.
Assuming the above function exists -
... x = preIncrement(x) ...
Wont work for the "it's a statement" reason.
Other solutions start to get really messy.
x = preIncrement(x)
... x ...
Works fine, except when the original LSL code is something like this -
while (doOneThing(x++))
{
doOtherThing(x);
}
Which becomes a whole can of worms. Using tables in the function -
function preIncrement(x) x[1] = x[1] + 1; return x[1]; end
temp = {x}
... preincrement(temp) ...
x = temp[1]
Is even messier, and has the same problems.
Starting to look like I might have to actually analyse the surrounding code instead of just doing simple translations to sort out what the correct way to implement any given crement will be. Anybody got any simple ideas?
I think to really do this properly you're going to have to do some more detailed analysis, and splitting of some expressions into multiple statements, although many can probably be translated pretty straight-forwardly.
Note that at least in C, you can delay post-increments/decrements to the next "sequence point", and put pre-increments/decrements before the previous sequence point; sequence points are only located in a few places: between statements, at "short-circuit operators" (&& and ||), etc. (more info here)
So it's fine to replace x = *y++ + z * f (); with { x = *y + z * f(); y = y + 1; }—the user isn't allowed to assume that y will be incremented before anything else in the statement, only that the value used in *y will be y before it's incremented. Similarly, x = *--y + z * f(); can be replaced with { y = y - 1; x = *y + z * f (); }
Lua is designed to be pretty much impervious to implementations of this sort of thing. It may be done as kind of a compiler/interpreter issue, since the interpreter can know that variables only change when a statement is executed.
There's no way to implement this kind of thing in Lua. Not in the general case. You could do it for global variables by passing a string to the increment function. But obviously it wouldn't work for locals, or for variables that are in a table that is itself global.
Lua doesn't want you to do it; it's best to find a way to work within the restriction. And that means code analysis.
Your proposed solution only will work when your Lua variables are all global. Unless this is something LSL also does, you will get trouble translating LSL programs that use variables called the same way in different places.
Lua is only able of modifying one lvalue per statement - tables being passed to functions are the only exception to this rule. You could use a local table to store all locals, and that would help you out with the pre-...-crements; they can be evaluated before the expression they are contained in is evauated. But the post-...-crements have to be evaluated later on, which is simply not possible in lua - at least not without some ugly code involving anonymous functions.
So you have one options: you must accept that some LSL statements will get translated to several Lua statements.
Say you have a LSL statement with increments like this:
f(integer x) {
integer y = x + x++;
return (y + ++y)
}
You can translate this to a Lua statement like this:
function f(x) {
local post_incremented_x = x + 1 -- extra statement 1 for post increment
local y = x + post_incremented_x
x = post_incremented_x -- extra statement 2 for post increment
local pre_incremented_y = y + 1
return y + pre_incremented_y
y = pre_incremented_y -- this line will never be executed
}
So you basically will have to add two statements per ..-crement used in your statements. For complex structures, that will mean calculating the order in which the expressions are evaluated.
For what is worth, I like with having post decrements and predecrements as individual statements in languages. But I consider it a flaw of the language when they can also be used as expressions. The syntactic sugar quickly becomes semantic diabetes.
After some research and thinking I've come up with an idea that might work.
For globals -
function preIncrement(x)
_G[x] = _G[x] + 1
return _G[x]
end
... preIncrement("x") ...
For locals and function parameters (which are locals to) I know at the time I'm parsing the crement that it is local, I can store four flags to tell me which of the four crements is being used in the variables AST structure. Then when it comes time to output the variables definition, I can output something like this -
local x;
function preIncrement_x() x = x + 1; return x; end
function postDecrement_x() local y = x; x = x - 1; return y; end
... preIncrement_x() ...
In most of your assessment of configurability to the code. You are trying to hard pass data types from one into another. And call it a 'translator'. And in all of this you miss regex and other pattern match capacities. Which are far more present in LUA than LSL. And since the LSL code is being passed into LUA. Try using them, along with other functions. Which would define the work more as a translator, than a hard pass.
Yes I know this was asked a while ago. Though, for other viewers of this topic. Never forget the environments you are working in. EVER. Use what they give you to the best ability you can.