Hitpoints

Hello everyone! :slight_smile: Could someone help me with adding lives for the “enemy”? I want the enemy to lose 1 hp after a collision.  I have many ideas but everyone doesn’t work.

This is code for enemy :

local function enemy2() local function miny2() local newMine2 = display.newImageRect("mina2.png", 35, 35) miny2HP = 3 physics.addBody(newMine2, "dynamic", {isSensor = true, radius=35}) newMine2.myName = "mina2" local whereFrom2 = math.random(5) if ( whereFrom2 == 1 ) then newMine2.x = 550 newMine2.y = 80 newMine2:setLinearVelocity(-150, 0) elseif ( whereFrom2 == 2 ) then newMine2.x = 550 newMine2.y = 148 newMine2:setLinearVelocity(-150, 0) elseif ( whereFrom2 == 3 ) then newMine2.x = 550 newMine2.y = 124 newMine2:setLinearVelocity(-150, 0) elseif ( whereFrom2 == 4 ) then newMine2.x = 550 newMine2.y = 176 newMine2:setLinearVelocity(-150, 0) elseif ( whereFrom2 == 5 ) then newMine2.x = 550 newMine2.y = 284 newMine2:setLinearVelocity(-150, 0) end end timer.performWithDelay( 1000, miny2, 20 ) end timer.performWithDelay( 10000, enemy2, 1 )

and this is my code for collision (bullet-enemy) :

local function onCollision2( event ) if ( event.phase == "began" ) then local obj1 = event.object1 local obj2 = event.object2 if ( ( obj1.myName == "bullet" and obj2.myName == "mina2" ) or ( obj1.myName == "mina2" and obj2.myName == "bullet" ) ) then display.remove( obj1 ) display.remove( obj2 ) end end end Runtime:addEventListener( "collision", onCollision2 )

I tried to separate obj1 and obj2 and add new functions, unfortunately my knowledge is too small, and I can’t find anywhere to explain for beginners how to do it.

If someone could take a moment to explain me and I would be very grateful :slight_smile:

Sorry for the mistakes, my english is not perfect :unsure:

You English is fine  :slight_smile: , but you haven’t explained exactly what is going wrong.  

My best guess is that the enemy is disappearing after 1 hit, instead of losing 1hp? If that’s the case, it’s because you are removing the enemy every time it collides with a bullet:

if ( ( obj1.myName == "bullet" and obj2.myName == "mina2" ) or ( obj1.myName == "mina2" and obj2.myName == "bullet" ) ) then display.remove( obj1 ) display.remove( obj2 ) end

What you want to do instead is delete only the bullet, and reduce the HP of the enemy by 1. If the HP reaches 0, then you remove the enemy as well.

Your enemy objects don’t have any HP value assigned in the code you have posted, instead you have “miny2HP = 3” and miny2HP is never used again.

You can add an HP property to each enemy as they are created just as you do with the myName property:

local newMine2 = display.newImageRect("mina2.png", 35, 35) newMine2.HP = 3

Then use that property in the collision:

if ( ( obj1.myName == "bullet" and obj2.myName == "mina2" ) or ( obj1.myName == "mina2" and obj2.myName == "bullet" ) ) then local theEnemy local theBullet if obj1.myName == "mina2" then theEnemy = obj1 theBullet = obj2 elseif obj2.myName == "mina2" then theEnemy = obj2 theBullet = obj1 end if theBullet then --always remove the bullet display.remove( theBullet ) end if theEnemy then --reduce HP by 1 theEnemy.HP = theEnemy.HP - 1 --and only remove enemy if health is 0 (or less) if theEnemy.HP \<= 0 then display.remove( theEnemy ) end end end

The code could be written in a more concise way, but hopefully this makes it clearer what is happening.

Edit: you may also need to put the object removal inside a 1 millisecond timer.performWithDelay call - if I remember correctly removing an object immediately inside a collision can cause problems.

Thank you very much for your answer. I did as you wrote, thanks to you I understand how it works :slight_smile: But I still have a problem with assigning HP, the first enemy has 3 HP, and after 3 collisions disappears, but the next ones sometimes can disappear after 1 collision. Did I put the given HP correctly? And could you explain to me what you meant to use timer.performWithDelay. What should it look like?
I ask a lot because thanks to the answers of experienced people I can learn the most :slight_smile:

local function miny2() local newMine2 = display.newImageRect("mina2.png", 35, 35) theEnemyHP = 3 physics.addBody(newMine2, "dynamic", {isSensor = true, radius=35}) newMine2.myName = "mina2" . . . local function onCollision2( event ) if ( event.phase == "began" ) then local obj1 = event.object1 local obj2 = event.object2 if ( ( obj1.myName == "bullet" and obj2.myName == "mina2" ) or ( obj1.myName == "mina2" and obj2.myName == "bullet" ) ) then local theEnemy local theBullet if obj1.myName == "mina2" then theEnemy = obj1 theBullet = obj2 elseif obj2.myName == "mina2" then theEnemy = obj2 theBullet = obj1 end if theBullet then display.remove(theBullet) end if theEnemy then theEnemyHP = theEnemyHP - 1 if theEnemyHP \<= 0 then display.remove(theEnemy) end end end end end Runtime:addEventListener( "collision", onCollision2 )

 

It’s behaving that way because you haven’t correctly implemented the health.

What you have done is created one single variable called “theEnemyHP”, which is being shared across ALL enemies. What you need to do is “attach” a health property to each instance of an enemy that you create, using the “.” symbol. This is something you are already doing when you set a “myName” property on each object. 

Notice the difference between my example:

local newMine2 = display.newImageRect("mina2.png", 35, 35) newMine2.HP = 3

and yours:

local newMine2 = display.newImageRect("mina2.png", 35, 35) theEnemyHP = 3

Do you see how I have attached something called “HP” to the instance of “newMine2”? In your code you simply have a variable called theEnemyHP which does not belong to any object. 

Regarding my comment about timer.performWithDelay, this is a function that you are already using in your first post but I will explain further. When a collision occurs, if you try to immediately remove one of the colliding objects using display.remove() you may experience runtime errors. A safe workaround is to say “don’t remove the object now, remove it on the next frame”.  

To do this, we use the timer.performWithDelay function which allows us to call a function after a delay as you are doing here:

timer.performWithDelay( 1000, miny2, 20 )

As you probably know, the arguments to this function are 

timer.performWithDelay(theTimeDelayInMilliseconds, theFunctionToCall, howManyTimesToCallTheFunction)

So to delay the removal of an object until the next frame you could change the collision function to do something like this:

if theBullet then local function removeBullet() display.remove( theBullet ) end timer.performWithDelay(1, removeBullet, 1) end if theEnemy then --reduce HP by 1 theEnemy.HP = theEnemy.HP - 1 --and only remove enemy if health is 0 (or less) if theEnemy.HP \<= 0 then local function removeEnemy() display.remove( theEnemy ) end timer.performWithDelay(1, removeEnemy, 1) end end 

Setting the time delay to 1 milliseconds forces Corona to execute the code on the next frame.

Thank you for the answer :slight_smile: , but I still have a problem with theEnemy.HP = 3. After using “.” I get the error “attempt to index global ‘theEnemy’” (a nil value), I tried to move theEnemy.HP = 3 to different places, but corona still has nothing to refer to. Could you still advise me what to do in this situation? Below I insert the code after entering “.HP” and without changing places

local function enemy2() local function miny2() local newMine2 = display.newImageRect("mina2.png", 35, 35) theEnemy.HP = 3 physics.addBody(newMine2, "dynamic", {isSensor = true, radius=35}) newMine2.myName = "mina2" local whereFrom2 = math.random(5) ... ... end timer.performWithDelay( 1500, miny2, 25 ) end timer.performWithDelay( 1500, enemy2, 1 ) --ZNIKANIE MIN LVL2 local function onCollision2( event ) if ( event.phase == "began" ) then local obj1 = event.object1 local obj2 = event.object2 if ( ( obj1.myName == "bullet" and obj2.myName == "mina2" ) or ( obj1.myName == "mina2" and obj2.myName == "bullet" ) ) then local theEnemy local theBullet if obj1.myName == "mina2" then theEnemy = obj1 theBullet = obj2 elseif obj2.myName == "mina2" then theEnemy = obj2 theBullet = obj1 end if theBullet then display.remove(theBullet) end if theEnemy then theEnemy.HP = theEnemy.HP - 1 if theEnemy.HP \<= 0 then display.remove(theEnemy) end end end end end Runtime:addEventListener( "collision", onCollision2 )

 

Sorry for taking so long to respond, you may have already worked out the problem. If you haven’t, I think you might benefit from working through some Lua/Corona tutorials as the error you’re describing is something very fundamental to how Lua works.

In your last post you have this at the top:

local newMine2 = display.newImageRect("mina2.png", 35, 35) theEnemy.HP = 3

That second line is saying “add a property called ‘HP’ to an object called ‘theEnemy’”. Do you see the problem here? There is no object called theEnemy at this point. What you needed to do is:

local newMine2 = display.newImageRect("mina2.png", 35, 35) newMine2.HP = 3

This adds a property called HP to your object called newMine2. The reason that the collision code refers to something called ‘theEnemy’ is because inside the collision function I made a variable called theEnemy to make it easier to read whether obj1 or obj2 is the bullet or the enemy. Outside of the function those names have no meaning, think of them as shortcuts. I could have also done this:

local function onCollision2( event ) if ( event.phase == "began" ) then local obj1 = event.object1 local obj2 = event.object2 if ( ( obj1.myName == "bullet" and obj2.myName == "mina2" ) or ( obj1.myName == "mina2" and obj2.myName == "bullet" ) ) then if obj1.myName == "bullet" then display.remove(obj1) elseif obj2.myName == "bullet" then display.remove(obj2) end if obj1.myName == "mina2" then obj1.HP = obj1.HP - 1 if obj1.HP \<= 0 then display.remove(obj1) end elseif obj2.myName == "mina2" then obj2.HP = obj2.HP - 1 if obj2.HP \<= 0 then display.remove(obj2) end end end end end

 

Thank you for your response. I am ashamed to admit it, but after several attempts I stopped and started reading the book “Corona SDK Mobile Game Development Beginners Guide” to learn from the beginning. Trying my hand at other types of games, but thanks to your answer I’m smarter about how to add properties and how to use them :slight_smile: Your code works perfectly, of course :slight_smile:

Based on your code, you should prolly look at Corona’s own getting started tutorials