3

I'm new to this whole thing, so please tell me if I'm doing this wrong of if there's a better spot for my question.

I'm making a game with basic systems for projectiles, npcs and items. This game will have multiple variants of each of these types. For the purposes of this example, there will be a Bullet projectile, a Rocket projectile, and a Pistol which shoots Bullets.

The only experience I have with such a system is inside Terraria's source code, where Items are all defined in a massive if-else tree, e.g.

else if (this.type == 467)
{
    this.name = "Fireball";
    this.width = 40;
    this.height = 40;
    this.aiStyle = 1;
    this.hostile = true;
    this.damage = 10;
}

The problem with this approach is (apart from the terrible mess it makes) that adding new items requires modifications to 4-5 different areas of code - especially if you want to add any extra effects, like giving off light, burning enemies etc.

I'm trying to do this in a much better format. I've come up with a simple system that with nested classes, e.g.

public class ProjectileData
{
    public class Bullet : Projectile
    {
        public float speed;
        public Bullet() : base()
        {
            this.type = ProjectileID.Bullet;
            this.damage = 25;
            this.origin = new Vector2(9.5f, 2.5f);
            speed = 10;
        }
        public override void AI()
        {
            this.velocity += new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * speed;
        }
        public override void OnHit()
        {
            Game.CreateExplosion(this.position);
        }
    }
    public class Rocket : Projectile
    { ... }
}

With this system, to create a bullet, instead of using something like

Projectile.CreateProjectile(int id, ...)
{
    Projectile projectile = new Projectile(id);
}
...
Projectile.CreateProjectile(ProjectileID.Bullet, ...);

I have

Projectile.CreateProjectile<T>(...) where T : Projectile, new()
{
    T projectile = new T(id);
}
...
Projectile.CreateProjectile<ProjectileData.Bullet>(...);

which looks and works nicely.

However, I've run into a problem: if I want to create an item that shoots a projectile, I can't reference it. For example, with the id-based system I could write something like

item.projectileType = ProjectileID.Bullet

but it isn't possible to assign a Class to a property like that.

I guess this boils down to 2 questions:

  • Is this the 'correct' way to pass data to an object or is there a better structure, with or without inheritance??
  • Is there a way to reference a class, and use that reference at runtime in a generic method call?
  • Whats the 'id' you pass into createProjectile and why cant you use that? – Ewan Apr 25 '16 at 7:28
  • That would be the method that I would use if I was using the messy id-based system, but I'm trying to find a different way of doing it. I need to pass something that lets me reference my ProjectileData.Bullet class. – FiveThree Apr 25 '16 at 7:32
  • (You also use it in your new T(id)) theres Type and typeof(Bullet) or you can use generics. But if you want to persist to a file or db you need to convert to a string or int at some point – Ewan Apr 25 '16 at 7:36
  • I could use a database file, but that's only practical if I was only loading number/string values, but I want to add hardcoded methods for movement patterns and effects.. I can't use typeof as it generic methods seemingly can't take Type values, only Types themselves (i.e. item.projectileType = typeof(ProjectileData.Bullet) and CreateProjectile<item.projectileType>(..) doesn't work). – FiveThree Apr 25 '16 at 7:44
  • 1
    I highly recommend to just hard-code everything. Design for game objects is generally extremely complicated. Especially if you want it extensible and modular. You are obviously building something that is way over your head. – Euphoric Apr 25 '16 at 8:02
2

Use the factory-pattern. A factory is an object which creates objects.

So when you have a weapon which shoot projectiles, pass a ProjectileFactory to it and leave the creation of the projectile to that class.

You can then have different classes which extend ProjectileFactory, like BulletProjectileFactory, ExplosiveProjectileFactory, GravityAffectedProjectileFactory, HomingProjectileFactory etc. which create bullets with different game mechanics.

For example, when you want to create a gun which shoots bullets which fly with a speed 8.0 pixels per tick, do 12 damage and use the sprite "bullet.png" you would write something like this:

 Weapon pistol = new Weapon( new BulletProjectileFactory( 8.0f, 12, "bullet.png") );

The fire() method of your weapon-class would then do something like

 projectileFactory.createProjectile();
  • Thankyou! That's a good option. I'm missing something here though: with such a Factory class, how would I change physics/hit effects. There isn't a way to pass a method into such a Factory, is there? – FiveThree Apr 25 '16 at 9:49
  • @FiveThree Since Java 8 you actually can treat methods like variables and pass them around. But that's really just syntactical sugar for something which was already possible before by having classes which implement an interface with just a single method. So you could also add another HitEffect class which you pass to the factory which then in turn passes it to all projectiles it generates. – Philipp Apr 25 '16 at 10:39
  • 1
    Since you're using C#, you could probably make the Weapon constructor take an Action<Projectile>. But there's really nothing wrong with taking a more specific object. – Magus Apr 26 '16 at 15:26
  • 1
    @FiveThree Sorry, I haven't noticed that this is C#, not Java. In C# you have delegates to pass around methods. As Magus said, the generics Action and Func from the .NET framework cover most use-cases pretty well, so there is rarely a reason to write your own delegate types. – Philipp Apr 26 '16 at 15:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.