I am new to Lua and am currently working on a project called Red VS Blue. It is basically for two teams, red team and blue team, and it functions as a scoreboard. If I click the left side, red gets 1 point. If I click the blue side, blue gets one point. The screen resolution is 320 x 480, but it widens according to the device.
Here is a screenshot:
http://i40.tinypic.com/5lqosk.jpg
this is my code:
display.setStatusBar(display.HiddenStatusBar);
local redscore = 0
local bluescore = 0
local background = display.newImage("images/background.png");
local redc = display.newImage("images/redc.png", 0, 0);
local bluec = display.newImage("images/bluec.png", 240, 0);
local redtext = display.newText(redscore, 55, 100, native.systemFont, 32*4);
local bluetext = display.newText(bluescore , 290, 100, native.systemFont, 32*4);
local function redt( event )
redscore = redscore + 1;
return true
end
redc:addEventListener( "tap", redt )
local function bluet( event )
bluescore = bluescore + 1;
return true
end
bluec:addEventListener( "tap", bluet )
redc and bluec are blank pictures that act as sensors
If you want to scale the picture to always fit perfectly the half of the screen, do this:
--[[
Take half the width of the device and adjust for larger screens, dividing by
the current width of the picture to produce the value it needs to be scaled
by to fit
]]
local wScale = ((display.layoutWidth/2) - display.screenOriginX) / redc.contentWidth
--Do the same for the height
local hScale = ((display.layoutHeight/2) - display.screenOriginY) / redc.contentHeight
--Scale the image
redc:scale(wScale,hScale)
--[[
Position the image. Since the default is a central reference point, you take half of
the image's width/height and add the adjustment for larger screens
]]
redc.x = (redc.contentWidth/2) + display.screenOriginX
redc.y = (redc.contentHeight/2) + display.screenOriginY
And to make the scoreboard update when the box is touched, structure your event listener as follows:
local function redt( event )
redscore = redscore + 1;
redtext.text = tostring(redscore)
return true
end
redc:addEventListener( "tap", redt )
Repeat for the blue box. Anything else you wanted to do with it?
Related
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! :)
I am currently working on a game what requires an inventory system. I cannot work out how to make an exit button for a display object I created.
Here is my code :
local Choose_slot = display.newImage("Images/choose_slot.png")
Choose_slot.x = centerX + 96
Choose_slot.y = 45
function choose_slot:tap ( event )
Inventory_Screen = display.newRect( centerX, centerY, 1500, 1500 )
Inventory_Screen:setFillColor( 0.3, 0.3, 0.3 )
end
local Exit_Button = display.newImageRect( "Images/Exit_Image.png", 32, 32)
Exit_Button.x = centerX + 255
Exit_Button.y = centerY - 135
function Exit_Inventory:tap ( event )
Inventory_Screen:remove()
Exit_Button:remove()
end
Exit_Inventory:addEventListener( "tap", Exit_Button)
Choose_slot:addEventListener( "tap", choose_slot)
Just for your Information, "Choose_slot" is an image that brings up the inventory. I would like to make it so when the "Inventory_Screen" comes up the "Exit_Inventory" button pops up and when you tap it, it removes the "Inventory_Screen" and the "Exit_Button" from the screen and goes back to screen you were on before you clicked inventory!
The picture is what left after I changed your code to this:
function Choose_slot:tap ( event )
Inventory_Screen = display.newRect( centerX, centerY, 1500, 1500 )
Inventory_Screen:setFillColor( 0.3, 0.3, 0.3 )
local function handleExitBEvent( event ) -- ERROR IS BELOW!
if ( "ended" == event.phase ) then
print( "Button was pressed and released" )
print( "Removing Inventory!" )
Choose_slot:removeSelf() -- This is not removing --
ExitB:removeSelf()
Inventory_Screen:removeSelf()
Inventory_Slot1:removeSelf()
end
end
It would be easier to this in a display group so you can remove all objects in a group with a line command.
Add the button and the function to your code as follows:
local function exitFunc(event)
Choose_slot:removeSelf() -- add whatever you want to remove
exitB:removeSelf() -- can use object.isVisible = false if you wanted but they maybe touchable still
end
exitB= widget.newButton
{
width=135,
height=60,
defaultFile = "whatever.png",
overFile = "whateverOver.png",
label = "Exit",
onPress = exitFunc,
}
I have a image declared at some specific location.
local deselectButton = display.newImage ( "images/nutritional info/deselectButton.png" )
deselectButton.x = display.contentWidth / 2 - 15
deselectButton.y = display.contentHeight / 2 - 172
deselectButton.id = "0"
nutriinfo:insert(nutriNavBar)
When I tap on that image, I want another image to be displayed. That is, this second image should fade in and out every time I click on the above image.
local dropDown1 = display.newImage ( "images/nutritional info/dropDown.png" )
dropDown1.x = display.contentWidth / 2 - 75
dropDown1.y = display.contentHeight / 2 - 65
dropDown1:setReferencePoint(display.TopCenterReferencePoint)
After your code, just do as follows... This may help you:
local function addListener()
deselectButton:addEventListener("tap",clickFunction)
end
local clickCount = 0
function clickFunction()
deselectButton:removeEventListener("tap",clickFunction)
clickCount = clickCount + 1
if(clickCount%2==1)then
-- show the image
transition.to(dropDown1,{time=200,x=dropDown1.x,y=dropDown1.y+100,alpha=1,onComplete=addListener}) -- or parameters as you like
else
-- hide the image
transition.to(dropDown1,{time=200,x=dropDown1.x,y=dropDown1.y-100,alpha=0,onComplete=addListener})
end
end
deselectButton:addEventListener("tap",clickFunction)
Note: The above code provides you both drop down as well as fade-in/out effect. But, if you need fade in and fade out effect only, you can eliminate y parameter from transition, and if you want the drop down type effect, you can eliminate the alpha parameter.
Keep coding............... :)
Is there a fill_parent or wrap_content function in corona just like in android? So it will fit the background image in every Screen Orientation. Or it have a different approach to to this.
thanks in advance.
There isn't any wrap_content because you don't have view groups in corona. You have simple groups but you can emulate wrap_content. There are also text modules wich will help you, check http://developer.coronalabs.com/code/
Yes you can implement fill_parent on every Screen Orientation:
local W = display.contentWidth
local H = display.contentHeight
local background
local function drawInterface()
background = display.newRect(0, 0, W, H)
background.x = display.contentCenterX
background.y = display.contentCenterY
background:setFillColor(99, 165, 204)
end
drawInterface()
local function removeInterface()
if background then background:removeSelf() end
end
local function onOrientationChanged (event)
print("orientation changed")
W = display.contentWidth
H = display.contentHeight
removeInterface()
drawInterface()
end
Runtime:addEventListener("orientation", onOrientationChanged )
we are doing a game with moving objects around frame-by-frame and also using accelerometer.
We have hooked on two events - about drawing the frame and for the acc.
The problem is, after we receive the acc event, we immediately put the x value in a variable.
Then we use this variable to move an object on the screen, but there is CONSIDERABLE slow down. ( I turn the phone, and after a second the object is moving properly, but a second is just way too much for a game, I expect immediate response).
What am I doing wrong? Is there another workaround to do this, or can I give some params to the accelerometer?
Unfortunately this is a serious problem - a real blocker. If this does not work, I have to find another solution (not Corona) for implementing the game.
Thanks in advance!!!
Danail
PS: here's some source:
local lastXGravity = 0
local function move(event)
eventTime=event.time
elapsedTime = eventTime - lastDrawTime
lastDrawTime = eventTime
xSpeed = lastXGravity
local xMoved = xSpeed * elapsedTime
object.x= object.x + xMoved
end
function acc(event)
lastXGravity = event.xGravity
end
Runtime:addEventListener("accelerometer", acc)
Runtime:addEventListener( "enterFrame", move )
I don't know anything about Corona development, but there are some general issues. First what is gravity containing? Just the gravity vector or total acceleration = gravity + userAcceleration? You will need to get userAcceleration = totalAcceleration - gravity or some member from event providing it directly, otherwise there is no chance.
If you have user acceleration, you need to integrate twice to get the position. See Equations of motion. In your case the code will be like:
velocity = userAcceleration * elapsedTime
position = 0.5*userAcceleration * elapsedTime^2
In general precise position detection by accelerometer and gyroscope is still an unresolved problem, so don't expect precise results. But if you are interested in just evaluating that there is an impulse in one direction, it might work. See for example Getting displacement from accelerometer data with Core Motion
The guys at Ansca's forum just got this out:
system.setAccelerometerInterval( 50 )
This didn't quite actually did the trick, but
system.setAccelerometerInterval( 100 ) -- warning - battery drainer!!
did it :)
I open-sourced my first Corona SDK-made game (which actually did really well) which uses Tilting in the same manner you describe (the more the tilt, the faster the movement and vice-versa).
It's called 'Tilt Monster' and you can download it here:
http://developer.anscamobile.com/code/tilt-monster
local isSimulator = "simulator" == system.getInfo("environment")
-- Accelerator is not supported on Simulator
if isSimulator then
-- Please display an Alert Box
end
-- Text parameters
local labelx = 50
local x = 220
local y = 95
local fontSize = 24
local frameUpdate = false
local xglabel = display.newText( "gravity x = ", labelx, y, native.systemFont, fontSize )
xglabel:setTextColor(255,255,255)
local xg = display.newText( "0.0", x, y, native.systemFont, fontSize )
xg:setTextColor(255,255,255)
y = y + 25
local yglabel = display.newText( "gravity y = ", labelx, y, native.systemFont, fontSize )
local yg = display.newText( "0.0", x, y, native.systemFont, fontSize )
yglabel:setTextColor(255,255,255)
yg:setTextColor(255,255,255)
y = y + 25
local zglabel = display.newText( "gravity z = ", labelx, y, native.systemFont, fontSize )
local zg = display.newText( "0.0", x, y, native.systemFont, fontSize )
zglabel:setTextColor(255,255,255)
zg:setTextColor(255,255,255)
y = y + 50
local xilabel = display.newText( "instant x = ", labelx, y, native.systemFont, fontSize )
local xi = display.newText( "0.0", x, y, native.systemFont, fontSize )
xilabel:setTextColor(255,255,255)
xi:setTextColor(255,255,255)
y = y + 25
local yilabel = display.newText( "instant y = ", labelx, y, native.systemFont, fontSize )
local yi = display.newText( "0.0", x, y, native.systemFont, fontSize )
yilabel:setTextColor(255,255,255)
yi:setTextColor(255,255,255)
y = y + 25
local zilabel = display.newText( "instant z = ", labelx, y, native.systemFont, fontSize )
local zi = display.newText( "0.0", x, y, native.systemFont, fontSize )
zilabel:setTextColor(255,255,255)
zi:setTextColor(255,255,255)
-- Create a circle that moves with Accelerator events
local centerX = display.contentWidth / 2
local centerY = display.contentHeight / 2
Circle = display.newCircle(0, 0, 20)
Circle.x = centerX
Circle.y = centerY
Circle:setFillColor( 0, 0, 255 ) -- blue
local textMessage = function( str, location, scrTime, size, color, font )
local x, t
size = tonumber(size) or 24
color = color or {255, 255, 255}
font = font or "Helvetica"
if "string" == type(location) then
if "Top" == location then
x = display.contentHeight/4
elseif "Bottom" == location then
x = (display.contentHeight/4)*3
else
-- Assume middle location
x = display.contentHeight/2
end
else
-- Assume it's a number -- default to Middle if not
x = tonumber(location) or display.contentHeight/2
end
scrTime = (tonumber(scrTime) or 3) * 1000 -- default to 3 seconds (3000) if no time given
t = display.newText(str, 0, 0, font, size )
t.x = display.contentWidth/2
t.y = x
t:setTextColor( color[1], color[2], color[3] )
-- Time of 0 = keeps on screen forever (unless removed by calling routine)
if scrTime ~= 0 then
-- Function called after screen delay to fade out and remove text message object
local textMsgTimerEnd = function()
transition.to( t, {time = 500, alpha = 0},
function() t.removeSelf() end )
end
-- Keep the message on the screen for the specified time delay
timer.performWithDelay( scrTime, textMsgTimerEnd )
end
return t -- return our text object in case it's needed
end -- textMessage()
local function xyzFormat( obj, value)
obj.text = string.format( "%1.3f", value )
-- Exit if not time to update text color
if not frameUpdate then return end
if value < 0.0 then
-- Only update the text color if the value has changed
if obj.positive ~= false then
obj:setTextColor( 255, 0, 0 ) -- red if negative
obj.positive = false
print("[---]")
end
else
if obj.positive ~= true then
obj:setTextColor( 255, 255, 255) -- white if postive
obj.positive = true
print("+++")
end
end
end
local function onAccelerate( event )
xyzFormat( xg, event.xGravity)
xyzFormat( yg, event.yGravity)
xyzFormat( zg, event.zGravity)
xyzFormat( xi, event.xInstant)
xyzFormat( yi, event.yInstant)
xyzFormat( zi, event.zInstant)
frameUpdate = false -- update done
-- Move our object based on the accelerator values
Circle.x = centerX + (centerX * event.xGravity)
Circle.y = centerY + (centerY * event.yGravity * -1)
-- Display message and sound beep if Shake'n
if event.isShake == true then
-- str, location, scrTime, size, color, font
textMessage( "Shake!", 400, 3, 52, {255, 255, 0} )
end
end
local function onFrame()
frameUpdate = true
end
-- Add runtime listeners
Runtime:addEventListener ("accelerometer", onAccelerate);
Runtime:addEventListener ("enterFrame", onFrame);
I hope, this code will help you.