Help with enemy-tank firing behavior

The video is HD, but DropBox doesn’t let you see it in HD unless you download the video. <_<
In addition to my other code that deals with ray creation and such, my conditional logic (which is all inside of my enemy fire function) is the following -

local hitFirst = E1Dray[1]

if E1Dray[1] == “Player” then
– firing code –

Now as shown in the video, when I place the user_tank behind a wall the ray, registers hits for the enemy-tank, radar, walls, and the user_tank but then when I moved the user_tank in a position where the only thing the ray would collide with is the user. It only registered one hit rather than multiple hits for the origin, radar etc…?
And to add to my confusion even more, I just tried it again with no obstacles between the ray’s origin and the user_tank, and it registered five hits. :blink: which I’m guessing are the enemy-tank, the radar, and the user_tank…

in reference to looping through all the hits to detect whether or not the enemy should fire, could I just assign an ‘obstacleclass to each object in my level/s and then check if “is there a wall object before the Player? If yes, then void the firing.”

Hi Saer,

One potential thing I see with these lines:

[lua]

local hitFirst = E1Dray[1]

if E1Dray[1] == “Player” then

[/lua]

Does the first index in “E1Dray” actually equal “Player”? If this is representing an object that the ray hits, then it’s going to return the Lua variable/ID of the object, not the name “Player”. If so, that’s why the logic isn’t working out. In that case, you’d need to apply a custom name or object type to everything when you create it, like this:

[lua]

Player.objType = “Player”

[/lua]

And then sense it like this in the ray cast:

[lua]

if E1Dray[1].objType == “Player” then

[/lua]

Or better yet, so you don’t need to assign this property to every object in the game, do this:

[lua]

if ( E1Dray[1].objType and E1Dray[1].objType == “Player” ) then

[/lua]

That way, it checks if the custom property “objType” even exists (which it probably wouldn’t on a wall or terrain object, unless you wanted to apply it to every single one), and if it does exist, then it checks if it’s equal to “Player”.

Brent

Here is what it looks like:
video

I removed a few objects to make sure it would still fire if the ray hit only the user_tank, but for some reason when I un-comment those two lines it doesn’t…?

– This turned out to be a long post – :rolleyes:

I had my user_tank set to user_tank.class = “Player”
My enemy-tanks, IGmines and a few other objects also had a defined .class

I added the .objType to the following objects - just to see if they would actually register in the terminal when hit:

[lua]
user_tank.objType = “PlayerTank”
user_turret.objType = “PlayerTank”
enemyB02.objType = “EnemyTank”
ET02radar.objType = “Radar”
[/lua]
And here is all of the code that deals with the ray

[lua]
    E1Dray = physics.rayCast( enemyB02.x - 50, enemyB02.y, user_tank.x, user_tank.y, “sorted” )
        
    local hitFirst = E1Dray[1]
    local hitX, hitY = hitFirst.position.x, hitFirst.position.y
        
    DRline = display.newLine( enemyB02.x - 50, enemyB02.y, user_tank.x, user_tank.y )
    DRline.width = 10
    DRline:setColor( 255,50,40,100 )
    DRline.blendMode = “add”
    level1group:insert( DRline )

    if E1Dray then
        – There’s at least one hit.
        print( "Hit count: " … tostring( #E1Dray ) )

        – Output all the results.
         for i,v in ipairs(E1Dray)  do
            print( "Hit: ", i, object_name, " Position: ", v.position.x, v.position.y, " Surface normal: ", v.normal.x, v.normal.y )
    end
        print( "The first object hit is: ",
        E1Dray[1].object, " at position: ", E1Dray[1].position.x, E1Dray[1].position.y, " where the surface normal is: ", E1Dray[1].normal.x, E1Dray[1].normal.y )

        else
        – There’s no hit.
    end
    if ( E1Dray[1].objType and E1Dray[1].objType == “PlayerTank” ) then
[/lua]

Using all that code, this is what the terminal window prints - based on the conditions in this photo

2013-06-21 12:33:52.560 Corona Simulator[23416:f03] Hit count: 2
2013-06-21 12:33:52.560 Corona Simulator[23416:f03] Hit:     1    nil     Position:     365.51693725586    387.89959716797     Surface normal:     0.56341761350632    0.82617217302322
2013-06-21 12:33:52.560 Corona Simulator[23416:f03] Hit:     2    nil     Position:     355.6755065918    393.59799194336     Surface normal:     0.89465683698654    -0.44675385951996
2013-06-21 12:33:52.561 Corona Simulator[23416:f03] The first object hit is:     table: 0x10e4fbad0     at position:     365.51693725586    387.89959716797     where the surface normal is:     0.56341761350632    0.82617217302322

The ray seems to only register a hit for the user_tank and user_turret (both with the same objType) which to me would make perfect sense, but it still won’t fire even though it’s hitting the PlayerTank. :wacko:

P.S. When I removed the - 50 from the ray and line (to make it originate from within the enemy-tank) the terminal still printed the exact same thing.

On line 16, where is “object_name” coming from?

Ummm… I’m not sure. I just copied all that print code from the RayCast docs
I assumed that it would get the name of the object that collides with the ray and then print that object’s name…

Thoughts?

Btw - I’d just like to say, if there are some documents or articles that will help me learn and figure out how to implement an idea/concept we discuss, please let me know. 
I in no way want to be perceived as someone who is looking to everyone else for all the answers and solutions to a problem I might encounter during the development process - as that is not even remotely my attitude.

– bump –
:ph34r:

Hi Saer,

I looked at the API documentation and I agree, that little part is a bit confusing. I’m going to update that code bit this week.

Anyway, to get the name of the object, you’ll have to access its “object” property and then get the name based on that, from however you specified the property on your object(s).

So, on line 16, if you attached your own “name” parameter to the necessary objects, it would be:

[lua]

print( "Hit: ", i, v.object.name, " Position: ", v.position.x, v.position.y, " Surface normal: ", v.normal.x, v.normal.y )

[/lua]

So, using:

[lua]v.object.name[/lua]

You should be able to determine which type of object (name) the raycast hit is representing.

Brent

Thanks for replying.

I use object.class for my objects.
I changed object_name to v.object.class and it now prints the name of whatever object the ray collides with. :smiley:

Sadly, using the code you suggested for my ray’s conditional statement, it still won’t fire even when the object.class is PlayerTank…?

-You’ll notice, in the code below, right after the ray’s if statement I have

print( “PlayerTank in range! Fire!” )

When I run the simulator, and the only object.class that hits the radar is PlayerTank, the enemy won’t fire…?
I don’t get any errors and it doesn’t print the statement I just mentioned.
Obviously it’s colliding with and registering the PlayerTank, but it won’t go beyond that if statement :huh:
[lua]
function fire( tower, user_tank )
    
    E1Dray = physics.rayCast( enemyB02.x, enemyB02.y, user_tank.x, user_tank.y, “closest” )
        
    local hitFirst = E1Dray[1]
    local hitX, hitY = hitFirst.position.x, hitFirst.position.y
        
    DRline = display.newLine( enemyB02.x, enemyB02.y, user_tank.x, user_tank.y )
    DRline.width = 10
    DRline:setColor( 255,50,40,100 )
    DRline.blendMode = “add”
    level1group:insert( DRline )

    if E1Dray then
        – There’s at least one hit.
        print( "Hit count: " … tostring( #E1Dray ) )

        – Output all the results.
         for i,v in ipairs(E1Dray)  do
            print( "Hit: ", i, v.object.class, " Position: ", v.position.x, v.position.y, " Surface normal: ", v.normal.x, v.normal.y )
    end
        print( "The first object hit is: ",
        E1Dray[1].object, " at position: ", E1Dray[1].position.x, E1Dray[1].position.y, " where the surface normal is: ", E1Dray[1].normal.x, E1Dray[1].normal.y )

        else
        – There’s no hit.
    end
    if ( E1Dray[1].class and E1Dray[1].class == “PlayerTank” ) then
    
    
        print(“PlayerTank in range! Fire!”)
        – solution: “firing solution” - the collision point between the tower’s bullet and the target
        – success: true if the tower can successfully fire on the target, false if the target will escape
        local solution, success = intercept( tower, user_tank, bulletSpeed )
                    –   
                local math_deg = math.deg  – localize ‘math.deg’ for better performance
                local math_atan2 = math.atan2  – localize ‘math.atan2’ for better performance
 
                – Calculates the angle between specified points A & B
                local function angleBetween( enemyB02X, enemyB02Y, user_tankX, user_tankY )
                local angle = ( math_deg( math_atan2( user_tankY-enemyB02Y, user_tankX-enemyB02X ) )+180) --; return angle
                    if ( angle < 0 ) then angle = angle + 360 end ; return angle % 360
                end

                local ang = angleBetween( enemyB02.x, enemyB02.y, user_tank.x, user_tank.y )
                enemyT02.rotation = ang
            –
        
        – only fire if the firing solution is successful
        if (success) then
                – calculate vx and vy
                local angle = angleOf( tower, solution )
                local pt = rotateTo( {x=bulletSpeed, y=0}, angle)
                local pt2 = mathapi.rotateAboutPoint( {x=enemyT02.x,y=enemyT02.y -65}, enemyT02, enemyT02.rotation - 90 )
                
                – create bullet
                Enemybullet = loader:createSpriteFromSHDocument(“Weapon5ex”, “GameFX”, “LevelAssets.pshs”);
                Enemybullet.x, Enemybullet.y, Enemybullet.solution = pt2.x, pt2.y, solution
                --bullet.x = pt2.x; bullet.y = pt2.y;
                physics.addBody(Enemybullet,{friction = 0, bounce = 1.0, density = 1.5, radius=10, isSensor = true})
                Enemybullet:scale(.65,.65)
                Enemybullet.class = “bullet”
                Enemybullet.isBulet = true
                Enemybullet.rotation = enemyT02.rotation + 90
                
                – fire bullet
                --bullet:applyForce( pt2.x - enemyT02.x, pt2.y - enemyT02.y, enemyT02.x, enemyT02.y )
                Enemybullet:setLinearVelocity(pt.x, pt.y)       
        end
    end
end
[/lua]

P.S. When I comment-out the if statement and the closing end, it fires whenever the user collides with the radar; so I know the code/logic for the actual firing works fine.

Again, you’ve stated the conditional check incorrectly. :slight_smile:  It should be:

[lua]

“E1Dray[1].object.class”

[/lua]

Because “object” is the internal table ID of the display object, and “class” is the property you’ve given it, as in “PlayerTank”.

Hi @Saerothir,

What is the current progress on this? I sort of lost track of what was going wrong. By the video, the raycasting seems to be working, but there must be some subtle thing I’m not noticing…

Brent

The main issue is getting the enemy tank to fire only when the ray collides with the user_tank.

As shown in the video, when the user_tank collides with the radar the enemy then casts a ray towards the user and when it hits the user_tank the enemy fires a bullet - all fine and good - however, the issue is when I un-comment the lines shown (in the vid) it still casts the ray but it no longer triggers the fire function, even when the user_tank is the only object the ray collides with.

Hi @Saerothir,

Are your enemy tanks physics objects too? If the raycast is originating from their center, it’s quite possible that it considers the enemy tank a “hit”, and a closer hit than the Player tank. I’d suggest you carefully check over your logic handling of the raycast, and perhaps experiment with other raycast filters in addition to “closest”.

As another side note, I would suggest that while the Player tank remains in the radar sensor, you run a continual timer of the increment on how fast the enemy can fire. On each execution of it, I’d cast a new ray toward the Player and check if the bullet is clear to fire (no obstacle in the path).

lol Wow… can’t believe I didn’t notice that! :rolleyes:

Thank you so much for all your help, Brent. I greatly appreciate it!

Here is a video of how it works:
video

–I’ll have to tweak it a little to take care of a few weird results that occur when the enemy-tank is moving, but that should be easy–
:smiley:

Hey Brent,
here is a video response

Just to clarify - At the beginning when I talk about how I thought the first hit was referring to the user tank, obviously since there was a wall between the user_tank and the enemy tank, that wall would be considered the closest first hit (assuming the enemy tank was not colliding with the ray).
Sorry for the rambling in the beginning of the vid; I hope in the end I was clear enough.

Looking very good :slight_smile: Good luck

Larry

Hi Saer,

This might be a really simple fix. I can’t tell in the video (the code text is blurry even if I expand the video), but what is the conditional line you’re using to detect “if the hit is Player”? Most signs indicate that the conditional logic is written incorrectly, not the raycasting logic.

Most likely, you will not be able to use the raycast filter “closest”. Because there are walls and the radar sensor, you’ll need to gather all hits, sorted from closest (to the enemy) to farthest. Then, you’ll need to loop through those and ask “is there a wall object before the Player? If yes, then void the firing.”.

So, let me know about the first question. I think it’s a simple logic error.

Brent

The video is HD, but DropBox doesn’t let you see it in HD unless you download the video. <_<
In addition to my other code that deals with ray creation and such, my conditional logic (which is all inside of my enemy fire function) is the following -

local hitFirst = E1Dray[1]

if E1Dray[1] == “Player” then
– firing code –

Now as shown in the video, when I place the user_tank behind a wall the ray, registers hits for the enemy-tank, radar, walls, and the user_tank but then when I moved the user_tank in a position where the only thing the ray would collide with is the user. It only registered one hit rather than multiple hits for the origin, radar etc…?
And to add to my confusion even more, I just tried it again with no obstacles between the ray’s origin and the user_tank, and it registered five hits. :blink: which I’m guessing are the enemy-tank, the radar, and the user_tank…

in reference to looping through all the hits to detect whether or not the enemy should fire, could I just assign an ‘obstacleclass to each object in my level/s and then check if “is there a wall object before the Player? If yes, then void the firing.”

Hi Saer,

One potential thing I see with these lines:

[lua]

local hitFirst = E1Dray[1]

if E1Dray[1] == “Player” then

[/lua]

Does the first index in “E1Dray” actually equal “Player”? If this is representing an object that the ray hits, then it’s going to return the Lua variable/ID of the object, not the name “Player”. If so, that’s why the logic isn’t working out. In that case, you’d need to apply a custom name or object type to everything when you create it, like this:

[lua]

Player.objType = “Player”

[/lua]

And then sense it like this in the ray cast:

[lua]

if E1Dray[1].objType == “Player” then

[/lua]

Or better yet, so you don’t need to assign this property to every object in the game, do this:

[lua]

if ( E1Dray[1].objType and E1Dray[1].objType == “Player” ) then

[/lua]

That way, it checks if the custom property “objType” even exists (which it probably wouldn’t on a wall or terrain object, unless you wanted to apply it to every single one), and if it does exist, then it checks if it’s equal to “Player”.

Brent