Multiple Enemy Class / Collision Detection Problem

Hi everyone-

I’ve been working on a game for quite some time now, on and off. I’m finally back on it and I’m running into a bit of an issue with my intended system for spawning enemies. Initially I created a class called “spawnEnemy” that would spawn an enemy with attributes passed to it - sprite to use, health, etc. However, as the game evolves I’m finding that a single spawnEnemy class would become way too large when you consider all of the unique behaviors that each enemy is supposed to express.

Now … my collision detection handles damage by calling a function inside of spawnEnemy called doDamageWithAmount, which works fine. My trouble is coming from the fact that instead of a single spawnEnemy class, I should be using enemyScout, enemyShooter, enemyBoss, etc. I have around 20 sprites (and counting) to which I intend to attribute unique behaviors and attributes so there will be quite a few classes to account for.

This of course means I cannot call doDamageWithAmount in spawnEnemy, since it needs to be called on each of those classes … at least that’s where my understanding of this system is. I’m hoping I’m wrong, which is why I’m posting this here. I would guess there’s a way to use a sort of parent class (spawnEnemy >> enemyScout) to handle that sort of thing, but I’m unclear on how.

I appreciate any help you guys can give me, I think this is the biggest hurdle I’ll face in development for some time, so you’d be helping me a huge amount. Thanks!

This looks to me like a classic case for Entity-Controller-System. Deriving a hierarchy of inheritance for all of your units leads to a situation where you end up with everything in one base class because everything needs to creep upwards in the hierarchy eventually. Apple’s GameplayKit includes classes to implement this system though it does tend toward an approach that is perhaps a bit outdated.

I highly recommend this tutorial: https://www.raywenderlich.com/119959/gameplaykit-tutorial-entity-component-system-agents-goals-behaviors

It specifically addresses spawning new units.

One way you might handle this is an enum in our Enemy class. Let’s say you have 20 or 30 possible enemy types. In your EnemyController class you can instantiate an instance of each and put it in an array or something convenient like that. EnemyController spawns the specific enemy, and handles running update on each in something like a for enemy in self.children type of function.

Then you could make an Enemy class with the appropriate enums. Switch on the types doing the correct setup for each. This could include the sprite, health, shields, and anything else you might want.

Let’s say you have a fighter, bomber, and carrier enemy ship type. Using enums you could instantiate each of those with any properties you might care to while retaining the basic functionality of an Enemy.

I have an upcoming tutorial on a 2D game that basically does this but it is about a month out. I hope that helps.

@aeberbach suggests GamePlayKit which I do think is worth a day playing with. My overall impression of it is not great though. I think it could use another year of feedback from real game developers to make it decent. It may be the solution to your problem but I have a hunch that it is fixing a loose nail with a complicated hydraulic cordless impact tool when a good o’le hammer would do the job.

I appreciate the responses everyone - I checked out that tutorial but it doesn’t seem to address my core issue since I didn’t see anything in there that handled collision detection. Where I stand right now is that I can spawn enemies with custom attributes without issue - specifying sprite, health, speed, etc. It’s the collision detection that’s causing an issue, and here’s why:

levelScene.m
- (void)didBeginContact:(SKPhysicsContact *)contact{

if(firstBody.categoryBitMask == primaryProjectileCategory && secondBody.categoryBitMask == enemyCategory){
   [self primaryWeaponProjectile:firstBody.node didCollideWithEnemy:(SKSpriteNode *)secondBody.node];
}

}

Above is the code for detecting any collision, and then routing to the appropriate function (below)

- (void)primaryWeaponProjectile:(SKNode *)weaponSprite didCollideWithEnemy:(SKSpriteNode *)body2{
	spawnEnemy *enemyClass = (spawnEnemy *)body2;
	[enemyClass doDamageWithAmount:l1_primaryWeaponDamage];
	[weaponSprite removeFromParent];
}

spawnEnemy.m

-(void)doDamageWithAmount:(float)damageAmount{
	_currentHealth -= damageAmount;
	int scoreAddAmount = 0;
	
	SKAction* pulseRed = [SKAction sequence:@[[SKAction colorizeWithColor:[SKColor redColor] colorBlendFactor:1.0 duration:0.1],
                                              [SKAction colorizeWithColorBlendFactor:0.0 duration:0.1]]];
	[enemySprite runAction:pulseRed];
    if(_currentHealth <= 0){
		[self makeExplosion:self.position];		
		[self removeFromParent];
	}
}

For the collision, I establish the enemy class, then call a function therein that will handle damage called doDamageWithAmount. The file called to do this is spawnEnemy.m . This is fine for something small scale, but as I said I won’t have enemies all behaving the same way so I might need to “do damage” to enemyScout enemyBoss, enemyShooter, etc.

I just don’t see a way to accommodate different enemy classes with the way my files are set up right now, which is why I was hoping I could have some sort of “enemy handler” which would call the appropriate class to do damage to the enemy. I think I’ll look into the class hierarchy suggestion randall put forward - it sounds closest to what I might need but I’ll have to research how to execute it. Having read the above do you agree that’s the route I should be taking?


Quick Update: Subclassing is looking promising, having read parts of this tutorial. I’m going to give it a go tomorrow - I still welcome suggestions though, my methods are certain to be out of date at this point, having started the project almost 2 years ago now.


New Update: Alright, I’ve tried subclassing and that seems to do the trick, so thank you all for your suggestions. I’m actually feeling pretty giddy at the moment at the prospect of having overcome this hurdle, will probably be working all weekend on the app now :smiley: .

@randall: I’m not sure why you deleted your post, but it was precisely what I needed - I pictured in my mind that subclassing was what I had to do, I just didn’t know how to do it or what to call it, funny how knowing the right term can help so much.

@aeberbach: I appreciate you pointing me toward GameplayKit - while it wasn’t what I was looking for it is good to know about so I can keep an eye on it for my next project, should I ever finish this one.

To jgnovak: Reading what you posted again I think it may have worked, it’s a little hard to tell since I’m not entirely clear on how Enums work or even what they really are. That said I’m looking forward to reading the tutorial - I’ve been hoping to find for more of those on here that deal 2D game development.

Sure thing. Since you are using Objective-C (props, I love Obj-C too!) here is an example project that is a Timberman clone in Obj-C and SpriteKit.

The code is written to be clear. I hope you find an example in there that gives you a hint that helps you make your game.

So, on enums you would basically do something like this.

typedef NS_ENUM(NSInteger, MonsterType) {
    Small,
    Medium,
    Large,
    Huge
};

Let’s say in your Monster class you have defined some properties. They could be something like health, damage, aggression etc.

@property CGFloat health;
@property CGFloat damage;
@property CGFloat aggression;
@property MonsterType type;

Your custom init for the Monster class could be something like this.

-(instancetype)initWithMonsterType:(MonsterType)monsterType {

    switch(monsterType) {
        case Small:
             SKTexture *texture =[[SKTextureAtlas atlasNamed:@"Sprites"] textureNamed:@"SmallMonster"];
             self = [Monster spriteNodeWithTexture:texture];
             _health = 100.0;
            _damage = 12.0;
            _aggression = 10.0;
           _type = Small;
           break;
       ... // others
    }
}

You can repeat this as needed in your Monster class. If you need specific behavior per type, you just switch on your method and do that behavior. For example, maybe your monsterDamaged: method does something different for each type.

-(void)monsterDamaged {
    switch(self.type) {
         case Small:
             // Do something for small monsters
             break;

         case Medium:
             // Do something for medium monsters
             break;

         case Large:
              // Do something for large monsters
              break;

         case Huge:
              // Do something for huge monsters
              break;
    }
}

Hope that gives you some ideas. If you have questions, this is one of the better forums on the subject.