🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Tower Defense - Upgrading Towers

Started by
13 comments, last by Monfik 2 years, 10 months ago

Monfik said:
I have Tower class

AttackTower class based on Tower

IceTower, FireTower, ArcherTower, CrossbowTower, StoneTower and some others based on Attack Tower.

The point is of all discussion above: Stop seeing “Tower” as a single thing. It's not 1 thing. It's a whole lot of things at the same time. Lots of sub-systems that work together to get tower behavior. You have the building itself as system. There is some cannon-barrel, there is a firing mechanism, and this is just the bare minimum. You could have power supply systems inside, heating system, munition storage, fire guidance systems, guard living quarters, and what not.

In the real world, if you have a tower and want a different tower, you don't tear down everything and rebuild it from scratch. No! Instead you just tear out some of the systems inside that are obsolete, and replace them by other systems inside (and only those). Everything else is just kept as is. An IceTower is not completely different from a FireTower, eg the basic tower building is the same at least, and likely other systems are the same as well.

You can do such a thing in software too. A “class Tower” is not a single thing with a lot of data fields. Instead, make a tower that has a lot of objects, one for each subsystem that you have. When you change the tower, don't delete the whole tower, only change some of its objects inside.

Not an C# expert, but this is a direction you can take. Note I entirely made this up, it likely doesn't even work, but I hope you get the idea that you can change the tower by changing only some parts of it.

class AttackSystem { // Attack system of the tower.
    Graphics attackSystemGraphics; // Graphics of the attack system
}

class FireAttack extends AttackSystem {
    int fireDamagePerSecond;

    void attackEnemy(Enemy e) {
        // Damage enemy with fire attack at 'fireDamagePerSecond' rate.
    }
}

class IceAttack: extends AttackSystem {
    float slowPower;

    void attackEnemy(Enemy e) {
        // Damage enemy with ice attack at 'slowPower' rate.
    }
}

Subsystem with graphics that can attack an enemy.

class Tower {
    Graphics towerGraphics; // Graphics of the tower
    AttackSystem attackSystem;
    // You can have many more sub-systems here.

    // Attack an enemy.
    void attackEnemy(Enemy e) {
        attackSystem.attackEnemy(e);
    }

    // Paint the tower to the display.
    void draw(Display display) {
        towerGraphics.paint(display);
        attackSystem.attackSystemGraphics.paint(display);
    }

    // Modify display of tower.
    void changeTowerGraphics(Graphics tg) {
        towerGraphics = tg;
    }
    // Modify attack system of the tower.
    void changeAttackSystem(AttackSystem a) {
        attackSystem = a;
    }
}

See how the tower has an AttackSystem and towerGraphics object. You can change them with the “change” functions. Each kind of attack system can have its own set of variables.

Methods of the tower, like “draw” or “attackEnemy” don't do much, they just call the sub-systems. Inside the subsystems the actual request is handled.

Obviously, you can do this trick again if you like, have more systems, or make subsub-systems inside sub-systems.

Advertisement

Unity and Unreal (and Godot, and a number of other engines) already give you the “entity/component” part, although Unity tends to lean heavier towards sub-entities as the “attachable components.”

Anyway, you'd define a main entity / prefab / blueprint class, which is “Tower.” This would attach a number of sub-components, or sub-entities, for the different functions. Those components might in turn define some interface that lets the master tower talk to them in a cohesive manner.

In Unity, you would configure this as a couple of base prefabs (for the tower itself, and for its damage dealing parts) and then use prefab variants that base off of this to keep the appropriate look and animation assets for each kind. Similarly, the “damage dealer” sub-entity could be reconfigured based on what tower kind you have.

When you level up, you'll typically want to have the components be told by their tower about their new level, using some kind of interface or shared base class.

Note that “classes” and “prefabs” have to interact here – you have Tower, the class, and it has a member that's a TowerDamageDealer, the class, and then you have TowerFireDamageDealer, TowerArrowsDamageDealer, … as subclasses from TowerDamageDealer. Then you build the prefabs of Tower with different DamageDealer instances as the particular implementation of that subclass.

The “gain level” and perhaps “enemy detected” and some other events will be virtual methods on the TowerFireDamageDealer, or event handlers, such that the tower can tell the damage dealer about gaining a level, no matter which particular subclass it is.

enum Bool { True, False, FileNotFound };

@Monfik Okay, I think I understand a little more about how your game works. So in my game, there is a flamethrower and a machinegun. The flamethrower starts the target on fire, causing it to take FireDPS for 3 seconds. Bullets on the otherhand, cause the target to take 10 damage immediately. I could add another Ice Weapon that slows the target for 3 seconds if I wanted. How can I manage this? I used two things:

  1. A Messaging system descibed in this book
  2. A class of that can affect behavior of an object. I'm going to call it an Effector here. There's a base effector class, and then things like Fire, Ice, Poison, etc are derived from the base class.

First, when my game detects a collision between a weapon projectile and a target, it sends a message to the target that says something like:

  1. You got hit by a bullet.
  2. You got hit with fire
  3. You got hit with ice.

That's simple! I let the target decide how to respond to the message when it reads it.

The targets (robots) read messages in their Update(DeltaTime) routine. They read a message, and add the appropriate Effector to a container/list of effectors. If the message says the robot got hit with:

  1. Fire, add a Fire-Effector to the list of effectors.
  2. Bullet, subtract 10 units from health.
  3. Ice, add an Ice-Effector to the list of effectors.

The message is removed from the list and returned to a resource pool.

Also in the Update() routine : Loop thru each Effector using pEffector→Update(DeltaTime). If the effector expires, remove it from the list, and return to a resource pool.

Each Effector should be given a pointer to the target (pTarget), so it can modifiy it:

  1. Fire Effector: pTarget→UpdateHealth(-1.0* fDeltaTime); pTarget→AddSmoke(); pTarget→SetColor(RGB(255,0,0);
  2. Ice Effector: pTarget→UpdateVelocity(-1.0 fDeltaTime); pTarget→SetColor(RGB(0,0,255);

Simple! You can see that the effects can STACK. That is, you could have 2 Ice Effectors in the targets list, and its speed will be reduced 2X! If you have two Fire Effectors in the list, it will take 2X the damage and produce 2X the smoke.

-Scott

@scott8 Yea I have exactly the same system for fire and poison and I called it BaseEffect and FireEffect, IceEffect and goes on, and it works the same as yours :D

The main thing is changing behaviour after uptade. Think what if urs weapon would shots twice after upgrade? After next upgrade it would have zoom feature. The same thing is on me.
I have Archer Tower which at level 3 of upgrade add 1 archer on the tower more and then tower “shots twice”. @alberth and @hplus0603 guided me some with sub-systems and yea it is great. Im thinking right now about it and idk how I would upgrade my AttackSystem, becouse variables are in those systems, I dont have acces to them cause base class. Ok I can make virtual Upgrade() but u see, what to pass? Int or float ? Fire needs int, Ice needs float.

Ok I can make virtual Upgrade() but u see, what to pass? Int or float ? Fire needs int, Ice needs float.

What you pass in is “level” not “how much more do you get.”

The Ice component has a table that says “at level 1 I do X, at level 2 I do Y, at level 3 I do Z.”

The Fire component has a switch statement in “activate” that switches on whether it's level 1, 2, or 3, and does different things inside “Activate()” based on level.

Upgrade() just tells each component which level it's at.

(Or the component could know its Tower, and the Tower could have a Level property that you simply read each time you need it; that works too.)

enum Bool { True, False, FileNotFound };

@hplus0603 Thats rly helpfull, I think Im ready ? Thanks for everyone

This topic is closed to new replies.

Advertisement