I'm working on a game in Lua, and so far I have everything working with everything in one document. However, to better organize everything, I've decided to expand it into modules, and while I figure I could probably get it working more or less the same, I figure now might be an opportunity to make things a little more clear and elegant.
One example is enemies and enemy movement. I have an array called enemyTable, and here is the code in Update that moves each enemy:
for i, bat in ipairs(enemyTable) do
if bat.velocity < 1.1 * player.maxSpeed * pxPerMeter then
bat.velocity = bat.velocity + 1.1 * player.maxSpeed * pxPerMeter * globalDelta / 10
end
tempX,tempY = math.normalize(player.x - bat.x,player.y - bat.y)
bat.vectorX = (1 - .2) * bat.vectorX + (.2) * tempX
bat.vectorY = (1 - .2) * bat.vectorY + (.2) * tempY
bat.x = bat.x + (bat.velocity*bat.vectorX - player.velocity.x) * globalDelta
bat.y = bat.y + bat.velocity * bat.vectorY * globalDelta
if bat.x < 0 then
table.remove(enemyTable,i)
elseif bat.x > windowWidth then
table.remove(enemyTable,i)
end
end
This code does everything I want it to, but now I want to move it into a module called enemy.lua. My original plan was to create a function enemy.Move() inside enemy.lua that would do this exact thing, then return the updated enemyTable. Then the code inside main.lua would be something like:
enemyTable = enemy.Move(enemyTable)
What I'd prefer is something like:
enemyTable.Move()
...but I'm not sure if there's any way to do that in Lua? Does anyone have any suggestions for how to accomplish this?
Sounds like you just want the metatable of enemyTable to be the enemy module table. Lua 5.1 reference manual entry for metatables
Something like this.
enemy.lua
local enemy = {}
function enemy:move()
for _, bat in ipairs(self) do
....
end
end
return enemy
main.lua
local enemy = require("enemy")
enemyTable = setmetatable({}, {__index = enemy})
table.insert(enemyTable, enemy.new())
table.insert(enemyTable, enemy.new())
table.insert(enemyTable, enemy.new())
enemyTable:move()
Of course you can do it. For what I can see, your Move function processes the table you pass it as an argument and returns another table, in a functional programming fashion, leaving the first table immutate. You just have to set your Move function so that it knows it has to operate on your enemy table instead of creating a new table. So in your module write:
local enemy = {}
-- insert whatever enemy you want in the enemy table
function enemy.Move()
for _, bat in ipairs(enemy) do
--operate on each value of the enemy table
end
--note that this function doesn't have to return anything: not a new enemy table, at least
end
return enemy
And in your love.load function you can call
enemyTable = require "enemymodule"
Then you just have to call enemyTable.Move()
Related
I've been coding for about 2 years, but I am still terrible at it. Any help would be much appreciated. I have been using the following code to set my background image parameters, after updating to Xcode 7.3 I got the warning 'C-Style statement is deprecated and will be removed':
for var totalHeight:CGFloat = 0; totalHeight < 2.0 * Configurations.sharedInstance.heightGame; totalHeight = totalHeight + backgroundImage.size.height {...}
Just to clarify, I have looked at a few other solutions/examples, I have noticed that one workaround is to use the for in loop, however, I just can't seem to wrap my head around this one and everything I have tried does not seem to work. Again, any help would be much appreciated.
A strategy that always works is to convert your for loop into a while loop along the lines of this pattern:
for a; b; c {
// do stuff
}
// can be written as:
a // set up
while b { // condition
// do stuff
c // post-loop action
}
So in this case, your for loop could be written as:
var totalHeight: CGFloat = 0
while totalHeight < 2.0 * Configurations.sharedInstance.heightGame {
// totalHeight = totalHeight + backgroundImage.size.height can be
// written slightly more succinctly as:
totalHeight += backgroundImage.size.height
}
But you're right, the preferred solution when possible is to use for in instead.
for in is a bit different to the C-style for or while. You don't control the loop variable directly yourself. Instead, the language will loop over any values produced by a "sequence". A sequence is any type that conforms to a protocol (SequenceType) that can create a generator that will serve that sequence up one by one. Lots of things are sequences – arrays, dictionaries, index ranges.
There's a kind of sequence called a stride that you could use to solve this particular problem using for in. Strides are a bit like ranges that increment more flexibly. You specify a "by" value that is the amount to vary by each time around:
for totalHeight in 0.stride(to: 2.0 * Configurations.sharedInstance.heightGame,
by: backgroundImage.size.height) {
// use totalHeight just the same as with the C-style for loop
}
Note, there are two ways of striding, to: (up to but not including, like if you'd used <), and through: (up to and including, like <=).
One of the benefits you get with a for in loop is that the loop variable doesn't need to be declared with var. Instead, each time around the loop you get a fresh new immutable (i.e. constant) variable, which can help avoid some subtle bugs, especially with closure variable capture.
You still need the while form occasionally (for example there's no built-in type that allows you to double a counter each time around), but for much everyday use there's a neat (and hopefully more readable) way of doing it without.
Might be best to go with a while loop:
var totalHeight: CGFloat = 0
while totalHeight < 2.0 * Configurations.sharedInstance.heightGame {
// Loop code goes here
totalHeight += backgroundImage.size.height
}
I can track location fine pre - vizconnect using code like this:-
vrpn7 = viz.add('vrpn7.dle')
posTracker = vrpn7.addTracker('PPT0#WorldViz-PC', 0 )
and then
x,y,z = posTracker.getPosition()
but I now use the new vizconnect e.g.
vizconnect.go( 'vizconnect_hmd_ppt.py' )
I'm wondering what the recommended way is to then access the trackers from my main project '.py' file and particularly when I'm using a keyboard/mouse scenario to simulate movement for during program development.
Any advice would be most welcome.
Thanks
Actually it was pretty straightforward:
first check the names of the trackers using:
print( vizconnect.getTrackerDict() )
it may return something like this
'mouse_and_keyboard_walking'
along with some others e.g. inertia cube, then do
gTracker = vizconnect.getTracker( 'mouse_and_keyboard_walking' )
or
gTracker = vizconnect.getTracker( 'PPT0#WorldViz-PC' )
then periodically call (probably on a callback() ):-
x, y, z = gTracker.getPosition()
I have the following problem:
I have over 20 different models which I want to simulate one after another but I want to change the simulation directory each time.
Right now I'm manually changing directory after each simulation (from ./ModelOne to ./ModelTwo) and I'd like to know if there's a way to change it automatically when I initialize or translate the new model.
Regards
Nev
the best way is to write a script I think:
pathOfSave = {"E:\\work\\modelica\\SimulationResult\\Model1\\","E:\\work\\modelica\\SimulationResult\\Model2\\"};
nbSim = 2;
pathOfMod = { "MyModel.",
"MyModel.};
modelsToSimulate = { ""Model1" ,
"Model2"};
//If equdistant=true: ensure that the same number of data points is written in all result files
//store variables at events is disabled.
experimentSetupOutput(equdistant=false, events=false);
//Keep in the plot memory the last nbSim results
experimentSetupOutput(equdistant=false, events=false);
for i in 1:nbSim loop
//delete the result file if it already exists
Modelica.Utilities.Files.removeFile(pathOfSave + modelsToSimulate[i]);
//translate models
translateModel(pathOfMod[i]+modelsToSimulate[i]);
// simulate
simulateModel(
pathOfMod[i]+modelsToSimulate[i],
method="dassl",
stopTime=186350,
numberOfIntervals=nbOfPoi,
resultFile=pathOfSave + modelsToSimulate[i]);
end for;
You can also put the command cd("mynewpath") in the initial algorithm section, if you want it tobe attached to the model.
model example
Real variable;
protected
parameter String currDir = Modelica.Utilities.System.getWorkDirectory();
initial algorithm
cd("C:\\Users\\xxx\\Documents\\Dymola\\MyModelFolder");
equation
variable = time;
when terminal() then
cd(currDir);
end when;
end example;
In any case you can find all commands of dymola in the manual one under the section "builtin commands".
I hope this helps,
Marco
I'm using Box2D and Cocos2D for iOS.
From time to time, the game freezes and it is caused by an infinite loop on b2World::SolveTOI.
for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next)
{
// Invalidate TOI
c->m_flags &= ~(b2Contact::e_toiFlag | b2Contact::e_islandFlag);
c->m_toiCount = 0;
c->m_toi = 1.0f;
}
I have a game where a "hero" has to collect coins. Coins are bodies, and through the contact listener I'm adding bodies in an array in order to destroy them later when the array is full (count = 2).
Here is how I add bodies to the array:
+ (void) addBodyToDestroy:(b2Body *)body {
[toDestroyArray addObject:[NSValue valueWithPointer:body]];
}
And here is how I destroy them:
+ (void) destroyAllBodies {
b2World *world = [InGame getWorld];
for (NSValue *bodyValue in toDestroyArray)
{
b2Body *body;
body = (b2Body*)[bodyValue pointerValue];
world->DestroyBody(body);
body = NULL;
}
[toDestroyArray removeAllObjects];
}
What I find very weird is that it doesn't freeze every time, just sometimes, and I can't get to know what seemes to block...
Solution :
The body was added more than once into the array to be destroyed, so when the array was full, it was destroyed more than once, which caused the infinite loop.
b2Contact of the contact manager is linked list data.
So, for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next)
means start from m_contactList and loop until c->next meets NULL
I don't know how your contact listener works, but you have to pay attention to the timing of the world step, collision check and destroy.
Because, the contact listener is called each time when there's a contact while world is stepping. So if your contact listener is designed to store contact data, then you have to process all the contact data between world step and destroy.
(If you don't, there might be dangling pointers of the destroyed body in contact data)
If you want to destroy coins when they fill the array, you'd better check the array if there is same object.
for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next) is quite an unusual for loop, at least from my experience. I can clearly explain what it does, and maybe this will help you.
This is how a for loop works:
for( /*code called at beginning, usually to create a counting variable*/;/*code that is checked for a boolean value every loop. True means loop again, false means stop. Usually this is checking the value of the counting variable.*/;/*code that is called at the end of each loop. Usually this is advancing the counting variable*/) {
//Code inside the loop that does stuff many times
}
So for this loop:
The loop starts. a new b2Contact pointer named c is created and is assigned to the value of the m_contactList property of m_contactManager.
The loop checks the value of c and determines a value of either true or false. In this case, since c appears to be an instance of an object, it probably is checking whether c is nil or not.
At the end of each loop, c is set to point to the m_next member of c. This is quite strange as c is no longer m_contactList but something contained by m_contactList. Then again this could be perfectly normal. You should know what your code does.
So I have a very simple game going here..., right now the AI is nearly perfect and I want it to make mistakes every now and then. The only way the player can win is if I slow the computer down to a mind numbingly easy level.
My logic is having a switch case statement like this:
int number = randomNumber
case 1:
computer moves the complete opposite way its supposed to
case 2:
computer moves the correct way
How can I have it select case 2 68% (random percentage, just an example) of the time, but still allow for some chance to make the computer fail? Is a switch case the right way to go? This way the difficulty and speed can stay high but the player can still win when the computer makes a mistake.
I'm on the iPhone. If there's a better/different way, I'm open to it.
Generating Random Numbers in Objective-C
int randNumber = 1 + rand() % 100;
if( randNumber < 68 )
{
//68% path
}
else
{
//32% path
}
int randomNumber = GeneraRandomNumberBetweenZeroAndHundred()
if (randomNumber < 68) {
computer moves the complete opposite way its supposed to
} else {
computer moves the correct way
}
Many PRNGs will offer a random number in the range [0,1). You can use an if-statement instead:
n = randomFromZeroToOne()
if n <= 0.68:
PlaySmart()
else:
PlayStupid()
If you're going to generate an integer from 1 to N, instead of a float, beware of modulo bias in your results.