Creating enemies on Dusk

 Hello @Caleb, I am trying to implement enemies in my game based on what you said but there are somethings I could not understand.

 

The best way to implement enemies in a game that uses dusk is positioning the enemies from within Tiled?

If so, I guess the right steps to do it would be:

 

1- create the enemy on the map (would it be an object on the  object layer or would it be just one normal tile?)

2- Set some properties (ich property should I set?) 

3- create the enemy on the code and import the properties from tiled to the code.(I would equal the enemy var with the object var on Tiled?(How would I do that?))

4- make the enemy move (should I treat the move of the enemy in the same function for every enemy I have on the game?)

 

Is that right? Sorry for the ammount of questions.

Thank you in advance!!!

1-3. Since I posted that response, Dusk has changed significantly and the approach to enemies is different. Rather than positioning your objects in your map with properties, the better way to position them now is by creating objects in object layers at each enemy location. What you’ll want to do is add a listener to your object layer which creates and removes your enemies as they’re drawn and culled. I provide some code and explain this method a little ways down this page: https://forums.coronalabs.com/topic/42355-dusk-engine/page-16.

  1. You’ll likely use one function for this.

    local function moveEnemy(enemy) – Do some gloriously awesome enemy movement code end

Then, in your enterFrame listener, you can call that function on each enemy.

  • Caleb

Hello @Caleb

I am trying to implement the code you posted:

local function addObjectType(params)… and local objectType = {}…

Please correct me if I am wrong:

This code is responsible for finding all objects in object layer with the name “mySpecialObjectType”, and creating an object for it. This code will treat the culling by creating or removing the object when it goes offscreen, am I right?

I also saw that we call the function addObjectType(objectType) once per object, right?

Could you clarify the addObject Type function for me? Specially here: 

for layer in map.objectLayers() do layer.addObjectListener("type", params.objectType, "drawn", buildObject) layer.addObjectListener("type", params.objectType, "erased", function(event) params.objects[event.object.builtObject] = nil params.remove(event.object.builtObject) end)

I saw in another post that we do not use a custom property for the type of the object in Tiled, we use the type field in the object.

I am using the portuguese version of Tiled, so should I change on the layer.addObjectListener(“type”) to layer.addObjectListener(“tipo”)?

I have tried to run this 2 functions with a piece of code from @painconfess but I am receiving the following error:

Attempt to call field ‘addObjectListener’(a nil value) stack traceback:

main.lua:105:in function ‘addObjectType’

main.lua:117:in main chunck

I wonder why I am receiving this error.

Once I can make this work, how can I access each object, to make all of them move? Should I create a function and inside it make a for loop with objects and make each of them move?

Sorry for the ammount of questions.

Thank you in advance and sorry for the long post

  • The addObjectType function “registers” an object type on the map. You provide the way to draw the object and the way to remove the object, and then it tacks your functions onto Dusk’s normal object culling functions. So instead of drawing and erasing just a rectangle, Dusk draws and erases the rectangle “host object” and the object your object type code created. Since the function is used to register a type, you only need to call it once per object type (read page 16 I linked; I explain a lot of this there).

  • I implement the addObjectType function by using Dusk’s built-in culling listeners to call the object type’s draw and erase listeners. That’s what the code you wanted clarified does - it iterates through every object layer and uses Dusk’s addObjectListener() function to call the draw and erase functions when culling activates.

  • The Portuguese version of Tiled (and all other languages as well) still exports to English JSON, so no need to change “type.”

  • I believe you need to update to the latest version of Dusk to fix your addObjectListener nil value bug.

  • My addObjectType function stores each built object under the “objects” table in the object type with objects as keys, so you can use a k,v in pairs() loop to go through the table.

    for k, v in pairs(objectType.objects) do local object = k end

  • Caleb

Hello @Caleb, thank you for the clarification. The error with tha addObjectListener was happening because I didn’t have the latest version of dusk. I updated the version as you suggested and now it is working really fine. Thank you again.

Now I am facing an issue with collision of the enemy. First, on my objectType.build function I set the name of the enemy object like in the code below, so if in Tiled, my object has the name “enemy”, when its object is created programmatically, it will also have the same name. 

object.name= event.object.name

I am using the code you provided me some days ago for collision. The problem is that the enemy is not being removed when I use display.remove(object).

When I analyse "print("The tile’s name is " … tostring(tile.name) … “, and the object’s name is " … tostring(object.name)).”, I have the following: “the tile’s name is enemy and the object’s name is Player.” 

So, does it mean the code sees all the objects excpet the player, as tiles and not objects? And how would I remove the object enemy.

PS: I am using a module to load all the enemies. Would that change anything in my collision function?

local function onCollision(event) local tile, object = event.object1, event.object2 if not tile.tileX and not tile.tileY then tile, object = object, tile -- We mixed the tile and the object up; switch them end if(object.name=="enemy") then --How to remove the enemy???-- playerInAir = false end -- Use tostring() in case the name of either object is nil -- print("The tile's name is " .. tostring(tile.name) .. ", and the object's name is " .. tostring(object.name)) end Runtime:addEventListener("collision", onCollision)

Thank you in advance @Caleb!!!

UPDATE:

When I call the function objectType:remove(object) the transparent object is removed but not its image. I don’t know if it is correct but I guess the collision function is reading the object from tiled and erasing it, but not the object created on the objectType.build function. So should I hide the transparent object to avoid its collision?(also, what is the name of the trransparent object, host?)

On my terminal i have the following message when I start the game:

Warning: Object rotation is not 0; object will not be added to culling grid or culled.

But I didn’t even set a rotation for my object, and the default rotation is 0, right?

Thank you for your help!

UPDATE:

Hello @Caleb, I got it working but I don’t know if it was the better way to do it. Here is what I did:

First I used the following code to hide the transparent object:
 

dusk.setPreference("virtualObjectsVisible", false)

Then in my collision function I used:

if(tile.name=="enemy") then EL.remove(tile)  --EL is the objectType end

My question is, the build function creates an object or a tile? Because I had to compare tile.name instead of object.name in order to make the game work.

If it compares to the tile, what is the difference between use a tile or an object in Tiled?

I wonder if what I did is right because I don’t want to create a game without knowing why something works and others don’t.

Also if you could clarify just these lines of code, maybe what I am not understanding is inside the collision function.

 local tile, object = event.object1, event.object2 if not tile.tileX and not tile.tileY then tile, object = object, tile -- We mixed the tile and the object up; switch them end

Thank you for your help @Caleb.

That tile collision code was to identify tiles. Dusk gives each tile the ‘tileX’ and ‘tileY’ properties to keep track of where they are in the map, so you can hook into that to identify if what you’re colliding with is a tile. Objects don’t get those properties. That means you’ll have to use something else to see if what you’re colliding with is an object. Your name approach is what I would recommend - give your object a special name and check for it in your collision function.

The switching part of the code is because it’s not guaranteed which object will be object1 and which will be object2. We have to check and see which is the player and which is the tile or object. To begin with, we set the player and object to simply object1 and object2. Then we do a little check to see if we were right; if we weren’t, we switch the variables.

local function onCollision(event) local object, player = event.object1, event.object2 if object.name == "player" then player, object = object, player end if object.name == "enemy" then -- Do whatever you do when the player collides with an enemy here end end
  • Caleb

Hello @Caleb, I came with this after your clarification. It is working fine but I would like to confirm if this was what you meant before:

 

local function onCollision(event) local tile, object = event.object1, event.object2 if not tile.tileX and not tile.tileY then tile, object = object, tile -- We mixed the tile and the object up; switch them end if(tile.name=="tilee") then playerInAir = false--player can jump end local object, player = event.object1, event.object2 if object.name == "player" then player, object = object, player --check if object is player end if object.name == "enemy" then EL.remove(object) --call the remove function on our other module end end Runtime:addEventListener("collision", onCollision)

Is this the best way to do it?

Another point I forgot to ask you, I am still receiving the following error in my terminal everytime I open my game.

Warning: Object rotation is not 0; object will not be added to culling grid or culled.

 

But I didn’t even set a rotation for my object, and the default rotation is 0, right?

Do you know what could it be?

Something like that. The function is a little redundant, though; you could either split it up into two functions (collidePlayerAndObjects and collidePlayerAndTiles) or just assume everything’s an object and check for tiles as a separate step to fix that.

Also, for your rotation warning, make sure all your objects aren’t rotated at all. Dusk’s “object is rotated” code isn’t anything special - it just checks the value from Tiled. If it reports an object as rotated, Tiled encoded it as rotated, which means it’s rotated in some sense or another. Don’t forget to check all your objects.

  • Caleb

Hello @Caleb

I believe I don’t have any rotated object in my maps. 
I have already checked in Tiled, and I created just rectangle objects, with no rotation, they are parallel to the tiles. Do you know if somebody else faced this issue?

Can I create some kind of property on Tiled, like rotate:false? 

I also believe the object can’t be culled because of this, and if it worked I guess the performance of my game would be a little bit better, right?

Thank you a lot,

Carlos 

If that’s the case, can you please PM me the map Dusk claims has a rotated object in it? It could be that Tiled is reporting it in a weird way and I want Dusk to be able to handle that.

  • Caleb

Hello @Caleb, I am trying to send you the json file, but I am receiving a message telling me I am not permitted to upload this kind of file. Is the json the file you need, right? How could I send it to you?

Thank you in advance

You can just put it on DropBox (or another filesharing service) and PM me the link.

  • Caleb

1-3. Since I posted that response, Dusk has changed significantly and the approach to enemies is different. Rather than positioning your objects in your map with properties, the better way to position them now is by creating objects in object layers at each enemy location. What you’ll want to do is add a listener to your object layer which creates and removes your enemies as they’re drawn and culled. I provide some code and explain this method a little ways down this page: https://forums.coronalabs.com/topic/42355-dusk-engine/page-16.

  1. You’ll likely use one function for this.

    local function moveEnemy(enemy) – Do some gloriously awesome enemy movement code end

Then, in your enterFrame listener, you can call that function on each enemy.

  • Caleb

Hello @Caleb

I am trying to implement the code you posted:

local function addObjectType(params)… and local objectType = {}…

Please correct me if I am wrong:

This code is responsible for finding all objects in object layer with the name “mySpecialObjectType”, and creating an object for it. This code will treat the culling by creating or removing the object when it goes offscreen, am I right?

I also saw that we call the function addObjectType(objectType) once per object, right?

Could you clarify the addObject Type function for me? Specially here: 

for layer in map.objectLayers() do layer.addObjectListener("type", params.objectType, "drawn", buildObject) layer.addObjectListener("type", params.objectType, "erased", function(event) params.objects[event.object.builtObject] = nil params.remove(event.object.builtObject) end)

I saw in another post that we do not use a custom property for the type of the object in Tiled, we use the type field in the object.

I am using the portuguese version of Tiled, so should I change on the layer.addObjectListener(“type”) to layer.addObjectListener(“tipo”)?

I have tried to run this 2 functions with a piece of code from @painconfess but I am receiving the following error:

Attempt to call field ‘addObjectListener’(a nil value) stack traceback:

main.lua:105:in function ‘addObjectType’

main.lua:117:in main chunck

I wonder why I am receiving this error.

Once I can make this work, how can I access each object, to make all of them move? Should I create a function and inside it make a for loop with objects and make each of them move?

Sorry for the ammount of questions.

Thank you in advance and sorry for the long post

  • The addObjectType function “registers” an object type on the map. You provide the way to draw the object and the way to remove the object, and then it tacks your functions onto Dusk’s normal object culling functions. So instead of drawing and erasing just a rectangle, Dusk draws and erases the rectangle “host object” and the object your object type code created. Since the function is used to register a type, you only need to call it once per object type (read page 16 I linked; I explain a lot of this there).

  • I implement the addObjectType function by using Dusk’s built-in culling listeners to call the object type’s draw and erase listeners. That’s what the code you wanted clarified does - it iterates through every object layer and uses Dusk’s addObjectListener() function to call the draw and erase functions when culling activates.

  • The Portuguese version of Tiled (and all other languages as well) still exports to English JSON, so no need to change “type.”

  • I believe you need to update to the latest version of Dusk to fix your addObjectListener nil value bug.

  • My addObjectType function stores each built object under the “objects” table in the object type with objects as keys, so you can use a k,v in pairs() loop to go through the table.

    for k, v in pairs(objectType.objects) do local object = k end

  • Caleb

Hello @Caleb, thank you for the clarification. The error with tha addObjectListener was happening because I didn’t have the latest version of dusk. I updated the version as you suggested and now it is working really fine. Thank you again.

Now I am facing an issue with collision of the enemy. First, on my objectType.build function I set the name of the enemy object like in the code below, so if in Tiled, my object has the name “enemy”, when its object is created programmatically, it will also have the same name. 

object.name= event.object.name

I am using the code you provided me some days ago for collision. The problem is that the enemy is not being removed when I use display.remove(object).

When I analyse "print("The tile’s name is " … tostring(tile.name) … “, and the object’s name is " … tostring(object.name)).”, I have the following: “the tile’s name is enemy and the object’s name is Player.” 

So, does it mean the code sees all the objects excpet the player, as tiles and not objects? And how would I remove the object enemy.

PS: I am using a module to load all the enemies. Would that change anything in my collision function?

local function onCollision(event) local tile, object = event.object1, event.object2 if not tile.tileX and not tile.tileY then tile, object = object, tile -- We mixed the tile and the object up; switch them end if(object.name=="enemy") then --How to remove the enemy???-- playerInAir = false end -- Use tostring() in case the name of either object is nil -- print("The tile's name is " .. tostring(tile.name) .. ", and the object's name is " .. tostring(object.name)) end Runtime:addEventListener("collision", onCollision)

Thank you in advance @Caleb!!!

UPDATE:

When I call the function objectType:remove(object) the transparent object is removed but not its image. I don’t know if it is correct but I guess the collision function is reading the object from tiled and erasing it, but not the object created on the objectType.build function. So should I hide the transparent object to avoid its collision?(also, what is the name of the trransparent object, host?)

On my terminal i have the following message when I start the game:

Warning: Object rotation is not 0; object will not be added to culling grid or culled.

But I didn’t even set a rotation for my object, and the default rotation is 0, right?

Thank you for your help!

UPDATE:

Hello @Caleb, I got it working but I don’t know if it was the better way to do it. Here is what I did:

First I used the following code to hide the transparent object:
 

dusk.setPreference("virtualObjectsVisible", false)

Then in my collision function I used:

if(tile.name=="enemy") then EL.remove(tile)  --EL is the objectType end

My question is, the build function creates an object or a tile? Because I had to compare tile.name instead of object.name in order to make the game work.

If it compares to the tile, what is the difference between use a tile or an object in Tiled?

I wonder if what I did is right because I don’t want to create a game without knowing why something works and others don’t.

Also if you could clarify just these lines of code, maybe what I am not understanding is inside the collision function.

 local tile, object = event.object1, event.object2 if not tile.tileX and not tile.tileY then tile, object = object, tile -- We mixed the tile and the object up; switch them end

Thank you for your help @Caleb.

That tile collision code was to identify tiles. Dusk gives each tile the ‘tileX’ and ‘tileY’ properties to keep track of where they are in the map, so you can hook into that to identify if what you’re colliding with is a tile. Objects don’t get those properties. That means you’ll have to use something else to see if what you’re colliding with is an object. Your name approach is what I would recommend - give your object a special name and check for it in your collision function.

The switching part of the code is because it’s not guaranteed which object will be object1 and which will be object2. We have to check and see which is the player and which is the tile or object. To begin with, we set the player and object to simply object1 and object2. Then we do a little check to see if we were right; if we weren’t, we switch the variables.

local function onCollision(event) local object, player = event.object1, event.object2 if object.name == "player" then player, object = object, player end if object.name == "enemy" then -- Do whatever you do when the player collides with an enemy here end end
  • Caleb

Hello @Caleb, I came with this after your clarification. It is working fine but I would like to confirm if this was what you meant before:

 

local function onCollision(event) local tile, object = event.object1, event.object2 if not tile.tileX and not tile.tileY then tile, object = object, tile -- We mixed the tile and the object up; switch them end if(tile.name=="tilee") then playerInAir = false--player can jump end local object, player = event.object1, event.object2 if object.name == "player" then player, object = object, player --check if object is player end if object.name == "enemy" then EL.remove(object) --call the remove function on our other module end end Runtime:addEventListener("collision", onCollision)

Is this the best way to do it?

Another point I forgot to ask you, I am still receiving the following error in my terminal everytime I open my game.

Warning: Object rotation is not 0; object will not be added to culling grid or culled.

 

But I didn’t even set a rotation for my object, and the default rotation is 0, right?

Do you know what could it be?