Spawn 3 objects but no duplicates in a row - Corona SDK - lua

Im looking to spawn 3 objects (Red,Green,Blue) in seperate columns but should not duplicate. So somehow Im looking for it to check the colours in the other columns and place the one thats left over.
So if Blue and Red are already spawned, the last column will be a Green etc.
Should I need to specify specific orders inside a table and then everytime I spawn I just choose a random order from within that table, or is there a better way?
Cheers

You will always have to make sure you use the colour only once. How and when you do that is completely irrelevant.
Of course creating objects randomly is not very efficient as you would risk to create some you cannot use.
So best would be to create 3 different objects and remove one of them randomly every time or to spawn an object using a random colour, removed from a colour list.

You can create a list of colors and shuffle it. Something like that:
math.randomseed( os.time() )
local colors = {
{ 1,0,0 }, -- red
{ 0,1,0 }, -- green
{ 0,0,1 }, -- blue
}
local function shuffleTable( t )
local rand = math.random
assert( t, "shuffleTable() expected a table, got nil" )
local iterations = #t
local j
for i = iterations, 2, -1 do
j = rand(i)
t[i], t[j] = t[j], t[i]
end
end
shuffleTable( colors )
local px = display.contentCenterX
local py = display.contentCenterY - 200
for i = 1, #colors do
local rect = display.newRect( px, py + 100 * i, 200, 100 )
rect.fill = colors[i]
end

Related

Error Trying to Draw Platforms in Lua/Love2D

New to Lua/Game dev in general here. I'm trying to write a code to draw the background of my game. I have four different spritesheets for things related to setting:
Background - which contains the drawn "background image" of the game
Platforms - which contain the platform sprites of the game
objectsBig - which contain the relatively bigger drawn objects in the background (example rocks, seaweed, etc)
objectsSmall - which contain the relatively smaller drawn objects (smaller rocks, plants, etc)
I made a function, called generateQuads() which is in my Util.lua file, which, given a spritesheet, splices it up into the different sprites in the spritesheet and returns those sprites. It is as follows:
-- Function to generate quads/cut the spritesheet
function generateQuads(atlas, tileWidth, tileHeight)
local sheetWidth = atlas:getWidth() / tileWidth
local sheetHeight = atlas:getHeight() / tileHeight
local sheetCounter = 1
local quads = {}
-- for every line of the sprite sheet
for y = 0, sheetHeight - 1 do
-- for every piece in the width
for x = 0, sheetWidth - 1 do
-- quads generated will be the ones specified by tileWidth and tileHeight
quads[sheetCounter] = love.graphics.newQuad(x * tileWidth, y * tileHeight, tileWidth,
tileHeight, atlas:getDimensions())
sheetCounter = sheetCounter + 1
end
end
return quads
end
I also have a class called Map, which is meant to contain information for the Map. The following are the functions in it:
FUNCTION TO SET UP THE CLASS
function Map:init()
-- Load the background sprite into variable
-- Each is 500x500, total is 1500x500
self.background = love.graphics.newImage('Graphics/platform/background/full.png')
-- Load all the ground platforms into variable
-- Each is 60x30, total is 240x30
self.platforms = love.graphics.newImage('Graphics/platform/ground/all.png')
-- Load all the bigger misc objects into variable
-- Each is 15x20, total is 15x120
self.objectsBig = love.graphics.newImage('Graphics/platform/objects/15x20/all.png')
-- Load all the smaller misc objects into variable
-- Each is 15x15, total is 60x15
self.objectsSmall = love.graphics.newImage('Graphics/platform/objects/15x15/all.png')
-- Setting the size for each of the variables IN PIXELS
self.backgroundWidth = 500
self.backgroundHeight = 1500
self.platformWidth = 60
self.platformHeight = 30
self.objectsBigWidth = 15
self.objectsBigHeight = 20
self.objectsSmallWidth = 15
self.objectsSmallHeight = 15
-- Setting the map size IN TILES
-- EXPERIMENTAL, CHECK AGAIN
self.mapWidth = 432
self.mapHeight = 243
-- Setting the width of the map IN PIXELS (in relation to platforms)
self.mapWidthPixels = self.backgroundWidth
self.mapHeightPixels = self.backgroundHeight
-- Storing our map features
self.tiles = {}
-- Storing the camera points, starting from the bottom
-- The 243 is the VIRTUAL_HEIGHT, which we needed to subtract to account for the
-- offset of the screen
self.camX = 0
self.camY = self.backgroundHeight - 243
-- Cutting each of the spritesheets
self.backgroundSprite = generateQuads(self.background, self.backgroundWidth, self.backgroundHeight)
self.platformSprite = generateQuads(self.platforms, self.platformWidth, self.platformHeight)
self.objectsBigSprite = generateQuads(self.objectsBig, self.objectsBigWidth, self.objectsBigHeight)
self.objectsSmallSprite = generateQuads(self.objectsSmall, self.objectsSmallWidth, self.objectsSmallHeight)
-- 'Setting' the tiles for the map to be background
for y = 1, self.mapHeight do
for x = 1, self.mapWidth do
self:setTile(x, y, BACKGROUND)
end
end
-- 'Setting' the platform tiles for where we start
for x = 1, self.mapWidth do
self:setTile(x, self.mapHeight - 303, GROUND_MIDDLE)
end
end
FUNCTION TO 'SET' WHERE IN THE X, Y COORDINATE THE TILES SHOULD BE
function Map:setTile(x, y, tile)
-- The table 'tiles' is going to store what tile should be in what x and y position
-- subtracting y by 1 so that it's 0 indexed, not 1 indexed
self.tiles[(y - 1) * self.mapWidth + x] = tile
end
FUNCTION TO RETURN WHAT TILE IS IN THE X, Y COORDINATE
function Map:getTile(x, y, tile)
-- This function is going to tell us what tile is set at this x and y position
return self.tiles[(y - 1) * self.mapWidth + x]
end
FUNCTION TO UPDATE
function Map:update(dt)
-- Moving the camera depending on the key being pressed
if love.keyboard.isDown('w') then
-- up movement, clamped between 0 and the other
self.camY = math.max(0, math.floor(self.camY - SCROLL_SPEED * dt))
elseif love.keyboard.isDown('a') then
-- left movement
self.camX = math.max(0, math.floor(self.camX - SCROLL_SPEED * dt))
elseif love.keyboard.isDown('s') then
-- down movement, subtracting VIRTUAL_HEIGHT to account for the screen offset
self.camY = math.min(self.backgroundHeight - VIRTUAL_HEIGHT, math.floor(self.camY + SCROLL_SPEED * dt))
elseif love.keyboard.isDown('d') then
-- right movement
self.camX = math.min(self.backgroundWidth - VIRTUAL_WIDTH, math.floor(self.camX + SCROLL_SPEED * dt))
end
end
FUNCTION TO RENDER
function Map:render()
for y = 1, self.mapHeight do
for x = 1, self.mapWidth do
-- Drawing the background first
love.graphics.draw(self.background, self.backgroundSprite[self:getTile(x, y)],
(x - 1) * self.backgroundWidth, (y - 1) * self.backgroundHeight)
love.graphics.draw(self.platform, self.platformSprite[self:getTile(x, y)],
(x - 1) * self.platformWidth, (y - 1) * self.platformHeight)
end
end
end
What I'm trying to do is splice up each spritesheet, and for set the background and bottom tiles so that I can see a bottom floor. I'm trying to do this through giving numbers to the components in my spritesheet, so that, for example, in a 1500x500 spritesheet for the background, the entire thing is considered 'one' sprite, and treated as such. Its number, 1, is given in a variable. Another example is a 240x30 platform spritesheet, where each 60x30 is considered 'one' sprite, and given a corresponding number, like so:
-- Know where the platforms are in the spritesheet
GROUND_LEFT = 1
GROUND_RIGHT = 2
GROUND_SMALL = 3
GROUND_MIDDLE = 4
-- Know where the backgrounds are in the spritesheet
BACKGROUND = 1
[...]
I want to then store each in the self.tiles list, so that I can access them at any time from the table (Do note that most of this is taken from the CS50 implementation of mario, and so I understand if I've gotten any concepts wrong). When I run this code, though, I get the following error:
Error
Error
Map.lua:135: bad argument #1 to 'draw' (Texture expected, got nil)
Traceback
[C]: in function 'draw'
Map.lua:135: in function 'render'
main.lua:48: in function 'draw'
[C]: in function 'xpcall'
Essentially, I just want to know a way in which I can set the bottom platform tiles, and then be able to iterate over the map and 'set' where the other object sprites should be. The background alone works fine, but once I added the:
-- 'Setting' the platform tiles for where we start
for x = 1, self.mapWidth do
self:setTile(x, self.mapHeight - 303, GROUND_MIDDLE)
end
and
love.graphics.draw(self.platform, self.platformSprite[self:getTile(x, y)],
(x - 1) * self.platformWidth, (y - 1) * self.platformHeight)
lines, the program wouldn't run.
Any help is appreciated!
EDIT: The line I'm getting an error on is the self.platform one.
i had the same problem before, and it turned out to be a typo when typing the variable. looking at your code, i saw that there was no self.platform, instead, there was self.platforms.
try:
love.graphics.draw(self.platforms, self.platformSprite[self:getTile(x, y)],
(x - 1) * self.platformWidth, (y - 1) * self.platformHeight)

Making a cubic ease-in-out curve dynamically switch its goal position

Using, Lua, I've made a function to cubically (easing in/"accelerating", then easing out/"decelerating" afterwards) interpolate from one number to another; SimpleSpline takes a number between 0-1 (the time the animation's been going, easiest put), and SimpleSplineBetween does the same, but keeps it in between two given minimum/maximum values.
function SimpleSpline( v )
local vSquared = v*v
return (3 * vSquared - 2 * vSquared * v)
end
function SimpleSplineBetween( mins, maxs, v )
local fraction = SimpleSpline( v )
return (maxs * fraction + mins * (1 - fraction))
end
It all works fine. However, I've run into a bit of an issue. I'd like this to be a bit more dynamic. For example, assume my "mins" is 0.5, and my "maxs" is 1, then I have a variable for time that I pass as V; we'll say it's 0.5, so our current interpolation value is 0.75. Now, let's also assume that suddenly, "maxs" is jerked up to 0.25, so now, we have a new goal to reach.
My current approach to handling situations like the above is to reset our "time" variable and change "mins" to our current value; in the above case 0.75, etc. However, this produces a very noticable "stop" or "freeze" in the animation, because it is being entirely reset.
My question is, how can I make this dynamic without that stop? I'd like it to transition smoothly from moving to one goal number to another.
local Start_New_Spline, Calculate_Point_on_Spline, Recalculate_Old_Spline
do
local current_spline_params
local function Start_New_Spline(froms, tos)
current_spline_params = {d=0, froms=froms, h=tos-froms, last_v=0}
end
local function Calculate_Point_on_Spline(v) -- v = 0...1
v = v < 0 and 0 or v > 1 and 1 or v
local d = current_spline_params.d
local h = current_spline_params.h
local froms = current_spline_params.froms
current_spline_params.last_v = v
return (((d-2*h)*v+3*h-2*d)*v+d)*v+froms
end
local function Recalculate_Old_Spline(new_tos)
local d = current_spline_params.d
local v = current_spline_params.last_v
local h = current_spline_params.h
local froms = current_spline_params.froms
froms = (((d-2*h)*v+3*h-2*d)*v+d)*v+froms
d = ((3*d-6*h)*v+6*h-4*d)*v+d
current_spline_params = {d=d, froms=froms, h=new_tos-froms, last_v=0}
end
end
Usage example according to your values:
Start_New_Spline(0.5, 1) -- "mins" is 0.5, "maxs" is 1
local inside_spline = true
while inside_spline do
local goal_has_changed = false
for time = 0, 1, 0.015625 do -- time = 0...1
-- It's time to draw next frame
goal_has_changed = set to true when goal is changed
if goal_has_changed then
-- time == 0.5 -> s == 0.75, suddenly "maxs" is jerked up to 0.25
Recalculate_Old_Spline(0.25) -- 0.25 is the new goal
-- after recalculation, "time" must be started again from zero
break -- exiting this loop
end
local s = Calculate_Point_on_Spline(time) -- s = mins...maxs
Draw_something_at_position(s)
wait()
end
if not goal_has_changed then
inside_spline = false
end
end

How to use bounding box in Love2d?

I've been using some extremely bulky code to detect collision between simple objects, and I've heard about bounding boxes. I can't find any tutorials on how to use it, so I'm asking about how to use it. Here is how I detect collision:
function platform.collision()
if player.x + player.width / 2 <= platform.x + platform.width and
player.x + player.width / 2 >= platform.x and
player.y + player.height <= platform.y + platform.height and
player.y + player.height >= platform.y then
The MDN has a rather concise article on 2D collision detection. Being the MDN, the examples are in javascript, but are easily translated to, and applicable in, any language - including Lua.
Let's take a look:
Axis-Aligned Bounding Box
One of the simpler forms of collision detection is between two rectangles that are axis aligned — meaning no rotation. The algorithm works by ensuring there is no gap between any of the 4 sides of the rectangles. Any gap means a collision does not exist.
Their example, translated to Lua:
local rect1 = { x = 5, y = 5, width = 50, height = 50 }
local rect2 = { x = 20, y = 10, width = 10, height = 10 }
if
rect1.x < rect2.x + rect2.width and
rect1.x + rect1.width > rect2.x and
rect1.y < rect2.y + rect2.height and
rect1.height + rect1.y > rect2.y
then
-- collision detected!
end
-- filling in the values =>
if
5 < 30 and
55 > 20 and
5 < 20 and
55 > 10
then
-- collision detected!
end
A live example, again in JavaScript, demonstrates this well.
Here's a quick (and imperfect) Love2D example you can throw into a main.lua and play around with.
local function rect (x, y, w, h, color)
return { x = x, y = y, width = w, height = h, color = color }
end
local function draw_rect (rect)
love.graphics.setColor(unpack(rect.color))
love.graphics.rectangle('fill', rect.x, rect.y,
rect.width, rect.height)
end
local function collides (one, two)
return (
one.x < two.x + two.width and
one.x + one.width > two.x and
one.y < two.y + two.height and
one.y + one.height > two.y
)
end
local kp = love.keyboard.isDown
local red = { 255, 0, 0, 255 }
local green = { 0, 255, 0, 255 }
local blue = { 0, 0, 255, 255 }
local dim1 = rect(5, 5, 50, 50, red)
local dim2 = rect(20, 10, 60, 40, green)
function love.update ()
if kp('up') then
dim2.y = dim2.y - 1
end
if kp('down') then
dim2.y = dim2.y + 1
end
if kp('left') then
dim2.x = dim2.x - 1
end
if kp('right') then
dim2.x = dim2.x + 1
end
dim2.color = collides(dim1, dim2) and green or blue
end
function love.draw ()
draw_rect(dim1)
draw_rect(dim2)
end
Oka explained it very well. This works for everything rectangular, not rotated and axis aligned. And you even already did it that way. This is great for buttons and the like!
But what I like doing is using (invisible) circles around objects and see if these collide. This works for everything where height is about the same as the width (which is the case for many sidescrolling platformers or top-down RPGs).
It's quite handy if you want to have the object centered at the current position. And it's especially helpful to simulate a finger on a touchscreen device, because a finger is quite a bit bigger than a mouse cursor. ;)
Here's an example on how to use this method. You can copy it as an actual game, it'll work.
--[[ Some initial default settings. ]]
function love.load()
settings = {
mouseHitbox = 5, -- A diameter around the mouse cursor.
-- For a finger (thouchscreen) this could be bigger!
}
objects = {
[1] = {
x = 250, -- Initial X position of object.
y = 200, -- Initial Y position of object.
hitbox = 100, -- A diameter around the CENTER of the object.
isHit = false -- A flag for when the object has been hit.
},
[2] = {
x = 400,
y = 250,
hitbox = 250,
isHit = false
}
}
end
--[[ This is the actual function to detect collision between two objects. ]]
function collisionDetected(x1,y1,x2,y2,d1,d2)
-- Uses the x and y coordinates of two different points along with a diameter around them.
-- As long as these two diameters collide/overlap, this function returns true!
-- If d1 and/or d2 is missing, use the a default diameter of 1 instead.
local d1 = d1 or 1
local d2 = d2 or 1
local delta_x = x2 - x1
local delta_y = y2 - y1
local delta_d = (d1 / 2) + (d2 / 2)
if ( delta_x^2 + delta_y^2 < delta_d^2 ) then
return true
end
end
--[[ Now, some LÖVE functions to give the collisionDetection() some context. ]]
function love.draw()
for i=1,#objects do -- Loop through all objects and draw them.
if ( objects[i].isHit ) then
love.graphics.setColor(255, 0, 0) -- If an object is hit, it will flash red for a frame.
objects[i].isHit = false
else
love.graphics.setColor(255, 255, 255)
end
love.graphics.circle("line", objects[i].x, objects[i].y, objects[i].hitbox/2)
end
end
-- You can use the following to check, if any object has been clicked on (or tapped on a touch screen device).
function love.mousepressed(x,y,button)
if ( button == 1 ) then
local i = objectIsHit(x,y) -- Check, if an object has been hit.
if ( i ) then
-- The object number 'i' has been hit. Do something with this information!
objects[i].isHit = true
end
end
end
function objectIsHit(x,y)
for i=1,#objects do -- Loop through all objects and see, if one of them has been hit.
if ( collisionDetected(x, y, objects[i].x, objects[i].y, settings.mouseHitbox, objects[i].hitbox) ) then
return i -- This object has been hit!
end
end
end
-- For the sake of completeness: You can use something like the following to check, if the objects themselves collide.
-- This would come in handy, if the objects would move around the screen and then bounce from each other, for example.
function love.update(dt)
if ( collisionDetected(objects[1].x, objects[1].y, objects[2].x, objects[2].y, objects[1].hitbox, objects[2].hitbox) ) then
-- The objects collided. Do something with this information!
end
end
As you can see, the collisionDetection() function is quite easy and intuitive to use.
Hopefully I could give you some more insight. And have fun with LÖVE 2D! :)

Lua: Making a collision system in a simple shooter and im having issues with removing the enemy object

I'm making a simple shooter in lua using love2d. For some reason when i launch the game the program thinks the enemy has been shot and doesn't spawn it. I think theres an issue on line 80. It seems to think enemy is nil at all times no matter what. I'm not getting any errors. Ill link to a pastebin with the code.
Edit: I've updated my code quite a bit and have solved the issue above. I think im checking for collision using the bounding box incorrectly. No matter where the bullet passes the enemy is never set to nil. I think it's because it checks with bullets.x instead of o.x but i cant check with o.x because its a local variable in the for loop earlier in the code.
http://pastebin.com/iwL0QHsc
Currently, your code does
if (CheckCollision) then
If you don't provide any parameters, it will check if the variable 'CheckCollision' exists. In this case, it does because you have declared it as a function on line 53, so every update 'enemy' will be set to nil.
if CheckCollision(x3,y3,x2,y2,w2,h2) then
Use this line but replace the variables with the variables of the respective entity/s.
Using this, you can use
if enemy then
In your draw call, which will check if 'enemy' exists.
Just by the way, in lua an if statement doesnt need to be within brackets.
if (x > 3) then
Functions exactly the same as
if x > 3 then
Edit:
In the new code you have provided, when you declare the function you are naming its arguments as variables that already exist. Usually, you put in some arbitrary variables you haven't used as arguments. Eg.
function test(a, b)
print(a + b)
end
Then to use it.
test(1, 2)
Or if you want to use variables.
var1 = 1
var2 = 2
test(var1, var2)
Using variables that already exist isn't too bad, it just means you can't use them. Using variables in a table, lua probably isn't too happy about.
So where you have
function CheckCollision(o.x,o.y,o.w,o.h, enemy.x,enemy.y,enemy.w,enemy.h)
return o.x < enemy.x+enemy.w and
enemy.x < o.x+o.w and
o.y < enemy.y+enemy.h and
enemy.y < o.y+o.h
end
Use something like this instead.
function CheckCollision(x1,y1,w1,h1, x2,y2,w2,h2)
return x1 < x2+w2 and
x2 < x1+w1 and
y1 < y2+h2 and
y2 < y1+h1
end
Alternatively, you could skip out on the parameters and have it hard coded.
function CheckCollision()
return o.x < enemy.x+enemy.w and
enemy.x < o.x+o.w and
o.y < enemy.y+enemy.h and
enemy.y < o.y+o.h
end
I'm not sure if this is the source of your error as I don't have access to a proper computer to try it but it is useful information anyway.
When you load/run a file in Lua, Lua looks through the whole file in a sequential manner once, so your line to check collisions only occurs upon main.lua's loading, never to be looked at again.
As your code stands now, it only checks the collision of the enemy and bullet once
if CheckCollision(enemy.x,enemy.y,enemy.w,enemy.h,bullets.x,bullets.y,bullets.w,bullets.h) then enemy = nil
end
If you put this into the love.update(dt) method, it will achieve your desired effect.
I'd like to note that once the enemy is set to nil (collision occurs), you will throw an error about attempting to index a nil value since your enemy variable is no longer a table.
Also noteworthy, these lines
bullets.x = o.x
bullets.y = o.y
in the for loop
for i, o in ipairs(bullets) do
cause your bullets to behave improperly (at least, I assume you don't wish for the behavior they have) Each time a new bullet is fired, it is added to the bullets table with the code
table.insert(bullets, {
x = player.x,
y = player.y,
dir = direction,
speed = 400
})
This puts each new table into the #bullets + 1 (the table's last index + 1) index of bullets. Since your for loop iterates over each bullet object in the bullets table, the last assignment that occurs is always on the last bullet in the table.
Let me try to explain this simpler.
Say a player fires two bullets. The first bullet firing will invoke the table.insert(...) call I mentioned before. So, our bullets table will look like this
bullets = {
x = 100,
y = 100, -- This is what you set player.x and player.y to in the start.
w = 15,
h = 15,
-- This is that new table we added - the 1st bullet fired.
{
-- This will all be inside it according to the table.insert(...) call.
x = 100, -- What player.x is equal to
y = 100, -- What player.y is equal to
dir = ... -- Who knows what it is, just some radians that you calculated.
speed = 400
}
}
Now, you used an ipairs(...) call which means that our for loop will only look at the tables inside bullets - smart thinking. But there is a problem with its implementation.
-- With our new table inside bullets, we will only have that table to look at for the entire loop. So, lets jump right into the loop implementation.
local i, o
for i, o in ipairs(bullets) do
-- This is fine. These lines look at the new table's x and y values and move them correctly.
o.x = o.x + math.cos(o.dir) * o.speed * dt
o.y = o.y + math.sin(o.dir) * o.speed * dt
-- This is the problem.
bullets.x = o.x -- Now, those x and y values in the bullets table are set to the new table's x and y values.
bullets.y = o.y
-- The rest of the loop works fine.
...
end
Now, for one new bullet, it works fine. Each update will update bullets.x and bullets.y correctly as the new bullet travels. But now lets considered the second bullet our player fired.
The new look of bullets is like this
bullets = {
x = 150, -- These are equal to the first bullet's values - for now, at least.
y = 150,
w = 15,
h = 15,
-- This 1st bullet is still here.
{
x = 150, -- Lets say the bullet has moved 50 pixels in both directions.
y = 150,
dir = ...
speed = 400
},
-- This is the new second bullet.
{
x = 100, -- Remember player.x and player.y are both 100
y = 100,
dir = ...
speed = 400
}
}
See where this is going yet? Lets jump to the for loop on the first iteration.
-- First iteration occurs. We're looking at the first bullet.
for i, o in ipairs(bullets) do
o.x = o.x + math.cos(o.dir) * o.speed * dt -- Lets say o.x = 160 now
o.y = o.y + math.sin(o.dir) * o.speed * dt -- and o.y = 160
bullets.x = o.x -- bullets.x = o.x, so bullets.x = 160
bullets.y = o.y -- bullets.y = o.y, so bullets.y = 160
...
end
But then we get to the second bullet...
-- Second iteration, second bullet.
for i, o in ipairs(bullets) do
-- Since it's the new table, o.x and o.y start at 100
o.x = o.x + math.cos(o.dir) * o.speed * dt -- Lets say o.x = 110
o.y = o.y + math.sin(o.dir) * o.speed * dt -- Lets say o.y = 110 as well
bullets.x = o.x
bullets.y = o.y
-- But now our bullets.x and bullets.y have moved to the new bullet!
-- The position of the 1st bullet is completely forgotten about!
...
end
And this is the problem. The way the loop is written currently, the program only cares about the most recently fired bullet because it is going to be placed last in the bullets table - it is checked and assigned to bullets.x and bullets.y last. This causes the collision check to only care if the most recently fired bullet is touching the enemy, none of the other ones.
There are two ways to fix this. Either evaluate the position of each bullet separately on collisions and add widths and heights to their tables, like this
-- When you add a bullet
table.insert(bullets, {
x = player.x,
y = player.y,
w = bullets.w,
h = bullets.h,
dir = direction,
speed = 400
})
-- When looking for collisions
local i, o
for i, o in ipairs(bullets) do
o.x = o.x + math.cos(o.dir) * o.speed * dt
o.y = o.y + math.sin(o.dir) * o.speed * dt
-- This will destroy an enemy correctly
if CheckCollision(enemy.x,enemy.y,enemy.w,enemy.h, o.x, o.y, o.w, o.h) then enemy = nil end
if (o.x < -10) or (o.x > love.graphics.getWidth() + 10)
or (o.y < -10) or (o.y > love.graphics.getHeight() + 10) then
table.remove(bullets, i)
end
end
This way you just move the collision checker's position to inside the loop and change its parameters.
The other way is to make class-like tables that can be "instantiated," whose objects' metatables point to the class-like table. Harder, but better practice and much easier to write methods and what not for. Makes generic inspections and evaluations of multiple players, enemies, bullets, etc. much easier as well.

Generate and manipulate a table of "complex" objects in CoronaSDK

I'm trying to generate 40 circles with text and I want to be able to use them and handle them later.
According to several post I found here and on corona forums, I'm doing it well but, in all the examples, is only with one "display" object... not with several so I'm not sure if it's possible or maybe it needs something different.
After some introduction... here the code:
function _.spawn(params)
local orb = display.newCircle( display.contentWidth/2, display.contentHeight/2, 40 ) -- creates the orb shape
orb.orbTable = params.orbTable --assign the internal table
orb.index = #orb.orbTable + 1 -- takes control of the index
orb.orbTable[orb.index] = orb -- assign a orb to the internal table
orb:setFillColor( unpack(params.color) ) -- change the color according to the rules above
orb.x = params.x -- control the X position of the orb
orb.y = params.y --control the Y position of the orb
orb.alpha = 1 -- borns with alpha = 0
orb.value = params.value -- assign the internal value of the orb (1-10)
--local orbText = display.newText( orb.value, orb.x+2, orb.y-5, "Aniron", 40 ) -- generates the value on the ball
--orbText.orbTable = params.orbTable
--orbText.index = #orbText.orbTable + 1
--orbText.orbTable[orbText.index] = orbText
--orbText:setFillColor( 0,0,0 )
--orbText.alpha = 1
--The objects group
orb.group = params.group or nil
--If the function call has a parameter named group then insert it into the specified group
orb.group:insert(orb)
--orb.group:insert(orbText)
--Insert the object into the table at the specified index
orb.orbTable[orb.index] = orb
return orb -- returns the orb to have control of it
end
The function to generate all the objects I need is:
function _.generateDeck()
for i = 1, 40 do -- loop to generate the 40 orbs
internalValue = internalValue + 1 -- counter to assigns the right values
if (internalValue > 10) then -- if the internal value is more than 10 starts again (only 1-10)
internalValue = 1
end
if (i >= 1 and i <= 10) then -- assign RED color to the first 10 orbs
completeDeck[i] = _.spawn({color = {196/255,138/255,105/255}, group = orbsGroup, x = 100, y = display.contentHeight / 2, value = internalValue, orbTable = completeDeck})
elseif (i >= 11 and i <= 20) then -- assigns YELLOW color to the next 10 orbs
completeDeck[i] = _.spawn({color = {229/255,214/255,142/255}, group = orbsGroup, x = 100, y = display.contentHeight / 2, value = internalValue, orbTable = completeDeck})
elseif (i >= 11 and i <= 30) then -- assigns BLUE color to the next 10 orbs
completeDeck[i] = _.spawn({color = {157/255,195/255,212/255}, group = orbsGroup, x = 100, y = display.contentHeight / 2, value = internalValue, orbTable = completeDeck})
elseif (i >= 11 and i <= 40) then -- assigns GREEN color to the next 10 balls
completeDeck[i] = _.spawn({color = {175/255,181/255,68/255}, group = orbsGroup, x = 100, y = display.contentHeight / 2, value = internalValue, orbTable = completeDeck})
end
end
end
After calling the function, it generates the circles correctly and I can read the value:
for i = 1, #completeDeck do
print("Complete Deck Value ("..i.."): "..completeDeck[i].value)
end
But if I uncomment the lines regarding the newText (orbText) then I'm not able to read the values... (value is a nil value)
I guess I need to create a displayGroup with the objects I want inside (circle and text), then "spawn" it, modifying the values? In that case... how can I refer to the object inside the displayGroup?
I think I'm mixing different concepts.
In Lua, objects are tables and to create an object composed of existing objects, we create a table with and add the objects to it as values. The code below creates an objects with orb and orbText which can be accessed by object.orb or object.orbText
function _.spawn(params)
local orb = display.newCircle(display.contentWidth/2, display.contentHeight/2, 40) -- creates the orb shape
orb:setFillColor(unpack(params.color))
orb.x = params.x
orb.y = params.y
orb.alpha = 1
local orbText = display.newText(param.value, param.x + 2, param.y - 5, "Aniron", 40)
orbText:setFillColor(0, 0, 0)
orbText.alpha = 1
-- create an object with orb and orbText as values
local object = {orb = orb, orbText = orbText}
-- add more values to the object
object.value = params.value
object.index = #params.orbTable + 1
params.orbTable[object.index] = object
return object
end
Now to use the object:
for i, object in ipairs(completeDeck) do
print(object.value)
end

Resources