Jump to content


Photo

Tower Defender


  • Please log in to reply
13 replies to this topic

#1 Gral92

Gral92

    GMC Member

  • GMC Member
  • 34 posts

Posted 25 February 2012 - 10:10 PM

Howdy

I started making a tower defence game recently. I watched some tutorials, but they all had similar problems. It seems that achieving some things I want to implement is going to be tricky. So here are some questions.

My biggest puzzle is... How to detect which enemy is closest to the base? I don't mean just the distance from friendly base, I mean the one who travelled largest distance from the spawn. I was thinking about making a variable for all enemies, which increases by their current speed every fraction of a second. So there would be a huge number saying how much an enemy has travelled, even considering slowdown effects and different speeds. However, that could cause trouble if I wanted multiple monster spawns. Any ideas? Is it even reliable for a single spawn?

Targetting enemies. The tutorials show bullets. That's horrible. Bullets can be dodged. Assuming I already know which enemy it should target from question above... Let's say there are 3 enemies within tower range ("distance_to_object(tower)<some number). How to quickly pick one that has largest "variable" from question above? All tutorials I've seen show only attacking the one closest to the tower and not closest to the goal.

Finally... I am currently using PATHS to show the enemies how they should travel. Fair enough. But what if I want to have an enemy that spawns enemies? Upon creation, they would act as if they are at the beginning of the path and would go off the road, possibly of the screen. So do I have to find a way other than paths, or is there an easy way to make the enemies just continue where they spawn?

Thanks in advance
  • 0

#2 greep

greep

    Menaces with Spikes

  • GMC Member
  • 2295 posts
  • Version:GM7

Posted 25 February 2012 - 11:07 PM

We'll need to know more about your game. E.g. largest distance travelled isn't even practical at all if you're doing a standard TD game which allows the player to place and delete tiles, since this can cause enemies to go "backwards". It may not work anyways if not, as enemies in most TDS stray from the path a lot.

However, I can answer one way of doing two anyways, not sure if it's the best. But here's what I do with this kind of thing:


with (Tower) //or freeze tower or whatever
{

     global.currentHighest = noone //these are going to get continually wiped out, so there's no need to waste emory by putting them in a tower ;)
     global.currentHighestNumber = -1

     with (EnemyParent)
     {
         if (distance_to_object(other) < (insert amount))
        {
             if (distance_travelled < global.currentHighestNumber)
             {
                  global.currentHighest = id
                 global.currentHighestNumber = distance_travelled
             }
        }
     }
     
    //now do something to currentHighest if it is not noone
}


  • 0

#3 Gral92

Gral92

    GMC Member

  • GMC Member
  • 34 posts

Posted 25 February 2012 - 11:52 PM

Well I'm not going to implement placing tiles during the game. I mean, the paths will be set for every level. So I think just adding up the speed would work. Allowing the player to edit the paths would mean writing some way to make enemies find paths my themselves

Anyway, I've been mostly using action icons and not the code so far, so I'm slightly confused about the code, but I'll likely figure it out later. But I'm concerned - will each tower calculate it's own "best target", picking only the mobs within it's range, when I use that code?

Edited by Gral92, 25 February 2012 - 11:53 PM.

  • 0

#4 FoxInABox

FoxInABox

    GMC Member

  • GMC Member
  • 5379 posts
  • Version:GM:Studio

Posted 26 February 2012 - 12:10 AM

since you are using a path, then you can use the path_position
if !instance_exists(enemy_parent_obj) exit; // no need to target if no enemies
if distance_to_object(enemy_parent_obj)>range exit; // no need to check if noone is near enough

var ins;
ins = instance_nearest(x,y,enemy_parent_obj)

with(enemy_parent_obj)
  if distance_to_object(other)<other.range // and the distance is less then the range of the tower
    if ins.path_position < path_position // if this one is further along the path
      ins = id;

target = ins; // you do now have the target

enemy spawning enemies:
// create it and set its position in the path to the same as its creator
with( instance_create(x,y,small_foe_obj) ) path_position = other.path_position;

The tutorials show bullets. That's horrible. Bullets can be dodged.

it is possible to find the meeting point for the enemy on the path and bullet, and then aim for that position

but then you might want to have a alarm that destroys the bullet when it should have hit and give its damage to its target if you want all to be hits .. accually, doing it that way makes it so you don't need any collision event at all.
  • 0

#5 RangerX

RangerX

    GMC Member

  • GMC Member
  • 908 posts
  • Version:GM8.1

Posted 26 February 2012 - 01:19 AM

About deciding wich ennemi is the closest (most dangerous) from your base...
You could classify them yourself since you know their strenght and weaknesses. You could put a "rating" variable in each of the different ennemies. Let's say you have 10 different ennemi, the less dangerous is ID0 and the most dangerous is ID10.

After that, in your code you have one more thing to rely on for your AI to determine its next action. Like if you use a "distance to point" function in your conditions as well as the rating I suggested you, you therefore can priorise X ennemy over Y ennemy even if both actually are at the same distance from your base.

Edited by RangerX, 26 February 2012 - 01:20 AM.

  • 0

#6 GMRescue

GMRescue

    GMC Member

  • GMC Member
  • 125 posts
  • Version:GM8

Posted 26 February 2012 - 11:18 AM

Dear Gral92,

To make the player shoot at the nearest enemy, you can use this code:

image_angle = point_direction(self.x,self.y,instance_nearest(x,y,obj_enemy).x,instance_nearest(x,y,obj_enemy).y);

Now, one of our players aims at the nearest enemy.

To make sure that a turret aims at different kinds of enemies, set the other enemies' parent as your basic enemy.

I hope I taught you something about instance_nearest.

Other GMC users already learned you something about it, but you might still use this reply.

- GMRescue
  • 0

#7 Gral92

Gral92

    GMC Member

  • GMC Member
  • 34 posts

Posted 28 February 2012 - 10:25 PM

Thanks everyone.

Now I've got a different problem. Not really relevant to tower defense. But it's weird!]

I used FoxInABox's code. Then I added this to the tower's Step code

if image_speed is equal to 1.5     
 if alarm[0] is equal to -1            
set Alarm 0 to firerate            
execute code:
target.alarm[0]=1;

Let me explain:
if image_speed is equal to 1.5 // image_speed changes to 1.5 when enemies are nearby. It works since the event does progress
if alarm[0] is equal to -1 // self-explanatory
set Alarm 0 to firerate // firerate is a tower variable for.. well, fire rate. It's 15 in this case
target.alarm[0]=1 // sets enemy alarm to 1. When their alarm hits 0, damage is done. That's my way to deal damage without projectiles

Expected result: When an enemy is nearby, every 15 loops, enemy's alarm 0 is set to 1 and damage is made

Actual result: Enemy takes damage once, then Alarm[0] of the tower is permanently stuck at 15, even when the enemy is out of range

It's like it keeps setting alarm 0 to 15 even though it shouldn't
Any clue about what's going on?
  • 0

#8 greep

greep

    Menaces with Spikes

  • GMC Member
  • 2295 posts
  • Version:GM7

Posted 28 February 2012 - 10:45 PM

Well for one thing, is your image_speed stuck too? If so, there's your problem, not the above code.
  • 0

#9 Gral92

Gral92

    GMC Member

  • GMC Member
  • 34 posts

Posted 29 February 2012 - 12:06 AM

It's not. Once the enemy goes off the tower's range, the animation stops. While he's in the range, it plays.

Edited by Gral92, 29 February 2012 - 12:28 AM.

  • 0

#10 greep

greep

    Menaces with Spikes

  • GMC Member
  • 2295 posts
  • Version:GM7

Posted 29 February 2012 - 01:44 AM

Maybe the code is just not written right. I don't use drag and drop, so you'll have to put this in an execute code and take out whatever tries this in D&D. Do

if (image_speed = 1.5)
{
    if (alarm[0] = -1)
   {
       alarm[0] = firerate
   } 
}
target.alarm[0]=1; // just your old code that is fine.


Alternatively, if that does not work, in your alarm event, at the end, do

if (image_speed = 1.5)
{
alarm[0] = firerate
}
  • 0

#11 Gral92

Gral92

    GMC Member

  • GMC Member
  • 34 posts

Posted 29 February 2012 - 10:10 PM

I managed it somehow. Thanks.

Next problems... I made a range sprite, which is just a green semi-transparent circle.

First of all, how do I make it display on the tower when the mouse cursor is there? Earlier I was experimenting with just displaying it permanently and skipping the mouse part. I made a Draw Event and put draw_sprite_stretched there. But but but then the tower just disappeared, it got replaced by the range circle. I know I can just add draw_sprite for normal sprite for the tower there, but the towers are animated when an enemy is nearby and I have to specify subimage as one of the parameters of draw_sprite, and since it works like step, it's stuck at whichever frame I specify. So maybe not using Draw event will be better?
  • 0

#12 FoxInABox

FoxInABox

    GMC Member

  • GMC Member
  • 5379 posts
  • Version:GM:Studio

Posted 29 February 2012 - 11:48 PM

it's stuck at whichever frame I specify. So maybe not using Draw event will be better?

you can use the build-in variables as the input for draw_sprite, that way you get the sprite you change to and animation frame..

draw_sprite(sprite_index, image_index, x, y) // just copy it directly and it will animate
if you want transparent, blending, scaling and rotation you will have to use:
draw_sprite_ext(sprite_index, image_index, x, y, image_xscale, image_yscale, image_angle, image_blend, image_alpha)

if you want some kind of tower selection marker, then I would suggest you let some other object handle that, so when you click on a tower it just adds its id to a variable:

if you give all towers a variable that hold the range sprite it shall use, then you can do it like this:

tower create event:
range = range_spr;
controll object draw event:
with( instance_position(mouse_x,mouse_y, tower_parent_obj) ) draw_sprite(range,0,x,y);
so whenever the mouse is above a tower, then it will try to draw the sprite located in variable range of the tower, oh, and instance_position is looking for the parent object .. just a object you make the parent for all the towers .. makes it easier to find a tower
  • 0

#13 Gral92

Gral92

    GMC Member

  • GMC Member
  • 34 posts

Posted 20 March 2012 - 08:36 PM

Hi there again, been a while

Anyway, here's another problem:

Let's say I have buildControl object. Inside it, I want to have a "selected" variable that is equal to the instance of the selected tower. So if I click on a tower, it will be "selected". Similarly to how the "target" variable works few posts above. I need this to be able to upgrade the towers easily.

I'm assuming I will need to do with() again, but I'm sort of confused on how it works
  • 0

#14 greep

greep

    Menaces with Spikes

  • GMC Member
  • 2295 posts
  • Version:GM7

Posted 20 March 2012 - 08:48 PM

Well you don't need with usually if you're working with singular objects, dot operators are easier. e.g.

Global left click of buildControl:

theItem = collision_point(mouse_x,mouse_y,TowerOrWhatever,true,true)

if (theItem >= 0)
{
select = theItem
}

And whenever you want to change a variable in select just do select.variablename = whatever. But first check if select even exists ;)

Edited by greep, 20 March 2012 - 08:49 PM.

  • 0




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users