Hi,
Ok, it’s driving me nuts.
I have a display group and I attached a display image and three text objects to it. When I create the display image, I store it’s reference and later on attach physic object to it. I have two groups of this group I just described.
Problem is the physic behavior is acting very weird. I push two different objects into these display groups with drag and each reacts differently based on the object it collided with.
Bug is, one of the display groups is not detected at all and the other one’s only upper half is working!
I suspect the problem is on how I create display groups and attach physic to it because it was fine before me transforming my code into OOP and doing these display groups.
So here are my main.lua and monsterclass which is those display groups:
[lua]-- Enabling debug mode.
–if (system.getInfo(“environment”)==“simulator”) then
– require(“mobdebug”).start()
–end
–[[
getmetatable(display.newRect(0, 0, 0, 0)).__tostring =
function(t) return ("{x=%s, y=%s}"):format(t.x, t.y) end
]]
–local proto, skip, meta = display.newRect(0, 0, 0, 0), {}, {}
–local metafields = “x,y,rotation,alpha,width,height,isVisible,xReference,yReference,xScale,yScale”
–for field in pairs(proto) do
– skip[field] = true
–end
– for field in metafields:gmatch("%w+") do
– meta[field] = true
–end
–getmetatable(proto).__tostring = function(t)
– local obj = {}
– for field in pairs(t) do
– if not skip[field] then
– obj[field] = t[field]
– end
– end
– for field in pairs(meta) do
– obj[field] = t[field]
– end
– return require(“mobdebug”).line(obj, {comment = false})
–end
–local boxClass = require(“box”)
–local newBox = boxClass.make();
–newBox:move();
local gameUI = require(“gameUI”); – We use it for dragging.
local showFPSModule = require(“showFPS”); – Display FPS.
–local widget = require “widget” – Buttons.
local physics = require( “physics” );
local monsterClass = require( “monsterclass” );
physics.start()
physics.setGravity(0, 0)
physics.setDrawMode( “debug” )
–physics.setDrawMode( “hybrid”)
–physics.setDrawMode( “normal”)
local DAY = 86400 – ( 24 * 60 * 60 )
local HOUR = 3600 – ( 60 * 60 )
local MINUTE = 60
math.randomseed(os.time()) – A better seed?
local weNeedToGoToNextWave = false;
local numberOfActiveFoods = 0;
local waveIndex = 1;
– Monsters (regions)
local monster_yellow;
local monster_purple;
– Spawn points.
local spawnPointTop = {x = 210, y = 50};
local spawnPointBottom = {x = 210, y = 200};
–[[
local myButton = widget.newButton{
id = “btn001”,
left = 100,
top = 200,
label = “Widget Button”,
width = 150, height = 28,
cornerRadius = 8,
onEvent = onButtonEvent
}
myButton.x = display.contentWidth * 0.5
myButton.y = display.contentHeight * 0.5
]]
showFPSModule.showFps();
display.setStatusBar(display.HiddenStatusBar)
– listener used by Runtime object. This gets called if no other display object
– intercepts the event.
local function printTouch( event )
print( “event(” … event.phase … “) (”…event.x…","…event.y…")" )
end
local function foodTouchHandler( event )
local t = event.target
– Print info about the event. For actual production code, you should
– not call this function because it wastes CPU resources.
– printTouch(event)
local phase = event.phase
if “began” == phase then
– Make target the top-most object
local parent = t.parent
parent:insert( t )
display.getCurrentStage():setFocus( t )
– Spurious events can be sent to the target, e.g. the user presses
– elsewhere on the screen and then moves the finger over the target.
– To prevent this, we add this flag. Only when it’s true will “move”
– events be sent to the target.
t.isFocus = true
– Store initial position
t.x0 = event.x - t.x
t.y0 = event.y - t.y
t:setLinearVelocity(0, 0)
elseif t.isFocus then
if “moved” == phase then
– Make object move (we subtract t.x0,t.y0 so that moves are
– relative to initial grab point, rather than object “snapping”).
t.x = event.x - t.x0
t.y = event.y - t.y0
elseif “ended” == phase or “cancelled” == phase then
display.getCurrentStage():setFocus( nil )
t.isFocus = false
end
end
– Important to return true. This tells the system that the event
– should not be propagated to listeners of any objects underneath.
return true
end
–[[
1-HowManyBombsToDrop;
2-TimeToWaitBetweenBombSpawns;
3-TimeAmountToRestBeforeStartingThisWave;
4-WaveExpirationTimer;
5-MinimumNumberOfBombsToKeepThisWaveActive;
]]
local waves={
[1] = {1, 0, 1, 5, 1},
[2] = {1, 0.5, 1, 5, 1},
[3] = {1, 0, 0, 5, 1},
[4] = {1, 0, 0, 5, 1},
[5] = {2, 1, 0, 5, 1},
[6] = {2, 0.5, 0, 5, 1},
[7] = {2, 0.5, 0, 5, 1},
[8] = {3, 0.2, 0, 5, 1},
[9] = {5, 1, 0, 5, 1},
[10] = {3, 0.2, 0, 5, 1},
}
local function initializeGame()
local borderCollisionFilter = { categoryBits = 1 }
local borderBodyElement = { friction=0.4, bounce=0.8, filter=borderCollisionFilter }
–borders of screen
local borderTop = display.newRect( 0, 0, display.contentWidth - 1, 1 )
borderTop:setFillColor( 0, 0, 0, 0) – make invisible
physics.addBody( borderTop, “static”, borderBodyElement )
local borderBottom = display.newRect( 0, display.contentHeight - 1, display.contentWidth - 1, 1 )
borderBottom:setFillColor( 0, 0, 0, 0) – make invisible
physics.addBody( borderBottom, “static”, borderBodyElement )
local borderLeft = display.newRect( 0, 1, 1, 480 )
borderLeft:setFillColor( 0, 0, 0, 0) – make invisible
physics.addBody( borderLeft, “static”, borderBodyElement )
local borderRight = display.newRect( display.contentWidth - 1, 1, 1, 480 )
borderRight:setFillColor( 0, 0, 0, 0) – make invisible
physics.addBody( borderRight, “static”, borderBodyElement )
– Background.
local background = display.newImageRect(“background.png”, display.contentWidth, display.contentHeight);
background.x = display.contentWidth / 2
background.y = display.contentHeight / 2
– local monsterPurpleCollisionFilter = { categoryBits = 8, maskBits = 1 } – collides with (1) only
– local monsterPurplePhysicSettings = { density = 0.2, friction = 0, bounce = 0.95, radius= 20.0, filter = monsterPurpleCollisionFilter }
– monster_purple = monsterClass.new(“Purple”, 100, 100);
monster_purple = monsterClass.new(“Purple”, display.contentWidth/2 - 180, display.contentHeight/2);
local squareShape = { -58,-157, 0, -157, 0, 157, -58, 157 } – make mouths smaller than monsters.
physics.addBody( monster_purple.visual, “static”, {shape = squareShape} )
– physics.addBody( monster_purple.visual, “static” )
monster_purple:ResetTimer();
– Yellow monster
– monster_yellow = display.newImageRect(“monster_yellow.png”, 111, 314);
– monster_yellow.x = display.contentWidth/2 + 180
– monster_yellow.y = display.contentHeight/2
– monster_yellow.myType = “Yellow”;
– squareShape = { -40, -157, 58, -157, 58, 157, -40, 157 } – make mouths smaller than monsters.
– physics.addBody( monster_yellow, “static”, { shape = squareShape} )
monster_yellow = monsterClass.new(“Yellow”, display.contentWidth/2 + 180, display.contentHeight/2)
local squareShape_yellow = { -40, -157, 58, -157, 58, 157, -40, 157 } – make mouths smaller than monsters.
physics.addBody( monster_yellow.visual, “static”, { shape = squareShape_yellow} )
monster_yellow:ResetTimer();
end
– @param event @class event
– @return
local function localDrag( event )
– isTouched is a flag to indicate that player previously touched this food object and if later on this
– object hits with a monster, then that collision is valid. So this prevents foods to get eaten by
– monsters when they move freely and accidentally collide with a monster.
– (We can toggle this to false after some time, if we want)
– event.target.isTouched = true;
local dragget_target = event.target;
dragget_target.isTouched = true;
– foodObject:addEventListener(“touch”, foodOnTouch);
– foodTouchHandler(event)
– gameUI.dragBody( event, {} )
– gameUI.dragBody(event, {maxForce = 100, frequency = 2} ) – slow, elastic dragging
– gameUI.dragBody(event, {maxForce = 100, frequency = 2, center = true } ) – slow, elastic dragging
return gameUI.dragBody(event, {maxForce = 20000, frequency = 10, dampingRatio = 0.9, center = true }) --very tight
– return true – The magic-- line
end
local function onPurpleFoodCollision(self, event)
if ( self.isTouched ) then
if (event.other.myType == self.myType) then
monster_purple.numberOfEatenFoods = monster_purple.numberOfEatenFoods + 1;
numberOfActiveFoods = numberOfActiveFoods - 1;
self:removeSelf();
self = nil;
elseif (event.other.myType == “Yellow”) then
monster_yellow:ResetEatenFoodCount();
numberOfActiveFoods = numberOfActiveFoods - 1;
self:removeSelf();
self = nil;
end
end
end
local function onYellowFoodCollision(self, event)
if ( self.isTouched ) then
if (event.other.myType == self.myType) then
monster_purple.numberOfEatenFoods = monster_purple.numberOfEatenFoods + 1;
numberOfActiveFoods = numberOfActiveFoods - 1;
self:removeSelf();
self = nil;
elseif (event.other.myType == “Purple”) then
monster_yellow:ResetEatenFoodCount();
numberOfActiveFoods = numberOfActiveFoods - 1;
self:removeSelf();
self = nil;
end
end
end
– @param x @class number
– @param y @class number
– @param foodType @class number
– @return
local function makeOneFoodObject(x, y, foodType)
— @classdef foodobject
– If you don’t pass anything, it will make a random food at center of the screen.
print(“Entering makeOneFoodObject”);
– foodElement = { friction=0.4, bounce=0.2, filter=borderCollisionFilter }
– local purpleFoodCollisionFilter = { categoryBits = 2, maskBits = 3 } – collides with (2 & 1) only
local purpleFoodCollisionFilter = { categoryBits = 2, maskBits = 1 } – collides with (1) only
local purpleFoodPhysicSettings = { density=0.2, friction=0, bounce=0.95, radius=35.0, filter=purpleFoodCollisionFilter }
– local yellowFoodCollisionFilter = { categoryBits = 4, maskBits = 1 } – collides with (4 & 1) only
local yellowFoodCollisionFilter = { categoryBits = 4, maskBits = 1 } – collides with (1) only
local yellowFoodPhysicSettings = { density=0.2, friction=0, bounce=0.95, radius=35.0, filter=yellowFoodCollisionFilter }
if not foodType then
foodType = math.random(1, 2)
end
local foodObject;
if 1 == foodType then
– foodObject = display.newImageRect(“food_purple.png”, 25, 33);
foodObject = display.newImageRect(“food_purple.png”, 42, 54);
foodObject.myType = “Purple”;
foodObject.collision = onPurpleFoodCollision;
foodObject:addEventListener(“collision”, foodObject); – TODO: Why we pass the same object as last parameter.
physics.addBody( foodObject, “dynamic”, purpleFoodPhysicSettings)
elseif 2 == foodType then
– foodObject = display.newImageRect(“food_yellow.png”, 25, 37);
foodObject = display.newImageRect(“food_yellow.png”, 42, 54);
foodObject.myType = “Yellow”;
foodObject.collision = onYellowFoodCollision;
foodObject:addEventListener(“collision”, foodObject);
physics.addBody( foodObject, “dynamic”, yellowFoodCollisionFilter)
end
foodObject:setReferencePoint(display.TopLeftReferencePoint);
– if no position is passed, we put it in center of the screen.
if not x and not y then
foodObject.x = display.contentCenterX;
foodObject.y = display.contentCenterY;
else
foodObject.x = x;
foodObject.y = y;
end
foodObject:setReferencePoint(display.CenterReferencePoint); --we need to revert reference point to center physic will move it by center.
foodObject:addEventListener(“touch”, localDrag);
– Appyling initial speed, if passed.
– foodObject.x = ball.x + vx
– ball.y = ball.y + vy
– Initial speed.
foodObject:setLinearVelocity(math.random(-20, 20) , math.random(-20, 20))
numberOfActiveFoods = numberOfActiveFoods + 1;
end
local function changeWave()
print("Entering changeWave while waveIndex is " … waveIndex);
– Randomly selecting a spawn point.
local currentSpawner;
local whichSpawnerToChoose = math.random(1, 2)
if 1 == whichSpawnerToChoose then
currentSpawner = spawnPointTop;
print(“we chose top spawn point”);
elseif 2 == whichSpawnerToChoose then
currentSpawner = spawnPointBottom;
print(“we chose bottom spawn point”);
end
– Spawning random number of foods.
local numOfFoodsToSpawn = math.random(1, 3)
for foodsToCreate = 1, numOfFoodsToSpawn do
makeOneFoodObject(currentSpawner.x - 100 + foodsToCreate * 50, currentSpawner.y)
end
weNeedToGoToNextWave = false;
end
local function UpdateHUD()
monster_purple.levelTextField:setText("Level: " … tostring(monster_purple.level));
monster_purple.GoalTextField:setText("Goal: " … tostring(monster_purple.numberOfEatenFoods) … “/” … tostring(monster_purple.goal));
monster_purple.mouthTimerTextField:setText("Time: " … tostring(monster_purple.HowMuchTimeIsLeftBeforeClosingMouthInSeconds));
monster_yellow.levelTextField:setText("Level: " … tostring(monster_purple.level));
monster_yellow.GoalTextField.text = "Goal: " … tostring(monster_purple.numberOfEatenFoods) … “/” … tostring(monster_purple.goal);
monster_yellow.mouthTimerTextField.text = "Time: " … tostring(monster_purple.HowMuchTimeIsLeftBeforeClosingMouthInSeconds);
end
timer.performWithDelay( 200, UpdateHUD, 0 )
local function main()
initializeGame();
–makeOneFoodObject(210, 260, 1);
–makeOneFoodObject(280, 260, 2);
end
– Calling entry point (just once).
main();
local function MainLoop()
if 0 == numberOfActiveFoods then
weNeedToGoToNextWave = true;
end
if true == weNeedToGoToNextWave then
changeWave();
end
– monster_purple:timer();
end
Runtime:addEventListener(“enterFrame”, MainLoop);
–timer.performWithDelay( 5000, MainLoop, 0 )[/lua]
[lua]local M = {} – table needed to return the file
local function ResetTimer(self)
self.whenToCloseMouth = os.time() + self.amoutOfTimneToCloseMouthInSeconds;
end
local function timer(self)
self.HowMuchTimeIsLeftBeforeClosingMouthInSeconds = self.whenToCloseMouth - os.time();
if ( self.HowMuchTimeIsLeftBeforeClosingMouthInSeconds <= 0 ) then
print(“Close mouth NAOW!”);
self:ResetTimer();
print( "Next mouth closeing time: " … tostring(self.whenToCloseMouth) )
self:CloseMouth();
end
end
–monsterObject:timer()
–monsterObject.mouthTimerTextField = display.newText("Time: " … tostring(monster_purple.HowMuchTimeIsLeftBeforeClosingMouthInSeconds), 15, 110, native.systemFontBold, 16)
local function CloseMouth(self)
if self.numberOfEatenFoods > self.goal then
print(“Purple monster ate more than goal number of foods, increasing level.”);
self.level = self.level + 1;
elseif self.numberOfEatenFoods < self.goal then
print(“Purple monster ate less than goal number of foods, decreasing level.”);
self.level = self.level - 1;
end
self.numberOfEatenFoods = 0;
self:ResetTimer();
end
local function ResetEatenFoodCount(self)
self.numberOfEatenFoods = 0;
end
– function
local function setText(self, string)
self.text = string or self.text
self:setReferencePoint(display.TopLeftReferencePoint)
self.x = self.originalX or self.x
self.y = self.originalY or self.y
end
function M.new(monsterType, xPos, yPos)
local newMonster = display.newGroup();
– newMonster.myType = monsterType;
newMonster.x = xPos;
newMonster.y = yPos;
newMonster.level = 1;
newMonster.goal = 3;
newMonster.numberOfEatenFoods = 0;
newMonster.amoutOfTimneToCloseMouthInSeconds = 7; – Mouth timer.
local visual;
if “Purple” == monsterType then
– visual = display.newImageRect(“monster_purple.png”, 116, 314);
– newMonster:insert(visual)
– newMonster.visual = visual;
newMonster.visual = display.newImageRect(newMonster, “monster_purple.png”, 116, 314);
elseif “Yellow” == monsterType then
– visual = display.newImageRect(“monster_yellow.png”, 111, 314);
– newMonster:insert(visual)
– newMonster.visual = visual;
newMonster.visual = display.newImageRect(newMonster, “monster_yellow.png”, 116, 314);
end
newMonster.visual.myType = monsterType
– Goal.
newMonster.levelTextField = display.newText(“Level: TEMP” , 15, 50, native.systemFontBold, 16);
local levelTextField = display.newText(“Level: TEMP” , 0, 0, native.systemFontBold, 16);
levelTextField.x = -50
levelTextField.y = -50
levelTextField.originalX = levelTextField.x
levelTextField.originalY = levelTextField.y
levelTextField:setReferencePoint(display.TopLeftReferencePoint)
newMonster:insert(levelTextField)
newMonster.levelTextField = levelTextField;
newMonster.levelTextField.setText = setText;
local GoalTextField = display.newText(“Goal: TEMP” , -50, -80, native.systemFontBold, 16);
GoalTextField.x = -50
GoalTextField.y = -80
GoalTextField.originalX = levelTextField.x
GoalTextField.originalY = levelTextField.y
GoalTextField:setReferencePoint(display.TopLeftReferencePoint)
newMonster:insert(GoalTextField);
newMonster.GoalTextField = GoalTextField;
newMonster.GoalTextField.setText = setText;
local mouthTimerTextField = display.newText(“Time: TEMP” , -50, -110, native.systemFontBold, 16);
mouthTimerTextField.x = -50
mouthTimerTextField.y = -110
mouthTimerTextField.originalX = levelTextField.x
mouthTimerTextField.originalY = levelTextField.y
mouthTimerTextField:setReferencePoint(display.TopLeftReferencePoint)
newMonster:insert(mouthTimerTextField);
newMonster.mouthTimerTextField = mouthTimerTextField;
newMonster.mouthTimerTextField.setText = setText;
– Assigning functions.
newMonster.ResetTimer = ResetTimer;
newMonster.timer = timer;
newMonster.CloseMouth = CloseMouth;
newMonster.ResetEatenFoodCount = ResetEatenFoodCount;
newMonster.visual:toFront();
return newMonster;
end
return M;[/lua] [import]uid: 206803 topic_id: 35380 reply_id: 335380[/import]
