1

我正在使用 Cocos2D for iOS,但您很可能不必熟悉 Cocos2D,只需 Obj-C 即可回答我的问题。

我在开发这个游戏时有一个敌人类,我已经使用了很长时间,但现在它已经达到了一个复杂的程度,我需要做一些事情来使它更有条理和可读性。

它目前的工作方式是这样的:我有一个敌人类,我分配了一定次数并插入到一个可变数组中。然后我可以随时翻阅那个可变数组。当敌人类被分配时,它也被引导初始化并传递一个敌人名称的字符串。在它的 init 中有一系列 if/if else 语句检查敌人名称并为其设置正确的值。这工作得很好,除了设计方面,当我添加越来越多的敌人时,查看所有这些名称会变得非常混乱。

我现在想做的是从所有不同敌人的敌人类别中分离出来。我需要访问敌人的属性,就像我访问该类的其他类型的敌人一样。

现在在敌人类初始化中,我有类似的东西:

-(id) initWithEnemy:(NSString *)kind {

    if([kind isEqualToString:@"enemyName"]){

    //set values

    }
    else if([kind isEqualToString:@"anotherEnemyName"]){

    //set values

    }

    //etc, etc..

}

现在我想让这个设定值发生在其他文件中。每个敌人的一个或一组头文件/主文件。所以在 initWithEnemy 中,我想也许我可以从传递的“kind”字符串中分配一个敌人名称类。不确定我是否可以使用 NSClassFromString。我已经对其进行了一些试验,但我不确定如何像以前那样访问类属性。即使我确实以与以前相同的方式访问属性,这是否意味着所有敌人名称类都必须具有所有相同数量的属性?

4

2 回答 2

2
  1. 而不是字符串,声明一个enum

    typedef enum {
        kEnemyInvalid = 0,
        kEnemyName1,
        kEnemyName2,
        [...]
    } EnemyType;
    
  2. Enemy为所有敌人类型创建一个具有全局属性的类。

  3. 为每种类型创建必要的敌人子类。一个班级可能会涵盖不止一种敌人类型。
  4. 创建一个函数(可能是一个类方法)

    Class EnemyClassFromEnemyType(EnemyType type) {
        switch (type) {
            case kEnemyName1:
               return [EnemyName1 class];
            case kEnemyName2:
               return [EnemyName2 class];
            default:
               return Nil;
        }
    }
    

    这个函数在敌人类型和实现它的类之间建立了联系。有一些方法可以让它更漂亮,我首选的方法是使用 X-Macros。

  5. 现在让我们创建一个工厂方法来创建敌人

    + (Enemy*)createEnemyWithType:(EnemyType*)enemyType {
        Class enemyClass = EnemyClassFromEnemyType(enemyType);
        return [[enemyClass alloc] initWithType:enemyType];
    }
    

使用 X-Macros 也一样

头文件

#define ENEMY_DEFINITIONS \
  ENEMY_DEFINITION(kEnemyInvalid, = 0, Nil) \
  ENEMY_DEFINITION(kEnemyName1,, [EnemyName1 class]) \
  ENEMY_DEFINITION(kEnemyName2,, [EnemyName2 class])

#define ENEMY_DEFINITION(name, intValue, enemyClass) name intValue, 

/**
 * Your enum declaration.
 */
typedef enum {
    ENEMY_DEFINITIONS
} EnemyType;

#undef ENEMY_DEFINITION

Class EnemyClassFromEnemyType(EnemyType type);
NSString* NSStringFromEnemyType(EnemyType type);

实施文件

#define ENEMY_DEFINITION(name, intValue, enemyClass) [name] = @#name,

NSString* EnemyTypeStringTable[] = {
    ENEMY_DEFINITIONS
}

#undef ENEMY_DEFINITION

NSString* NSStringFromEnemyType(EnemyType type) {
    return EnemyTypeStringTable[type]
}

#define ENEMY_DEFINITION(name, intValue, enemyClass) classTable[name] = enemyClass;

Class EnemyClassFromEnemyType(EnemyType type) {
    static Class* classTable = nil;

    if (classTable == nil) {
        classTable = malloc(sizeof(Class) * sizeof(EnemyTypeStringTable) / sizeof(NSString*));

        ENEMY_DEFINITIONS
    }

    return classTable[type];
}

#undef ENEMY_DEFINITION

使用 X-Macros 技术的美妙之处在于您将所有东西都放在一个地方,您可以轻松添加更多类型而无需更改任何其他内容。你会得到类似 Java 枚举的东西,因为枚举可以有属性。

于 2013-01-31T23:18:42.923 回答
2

您可以将敌人拆分为具有具体子类的抽象基类——这是一个好方法。但是请注意,使用组合而不是继承通常会更好 - 这是您将对象注入到持有者类中以建模某些东西的地方。否则,您可能会遇到一个问题,即您的敌人既是“怪物”又是“巫师”,而单一继承链不允许这样做。

这里有两种设计模式似乎很合适——它们都专注于将复杂的实例化规则与类本身解耦。一种是工厂模式,另一种是建造者模式。如果您拆分为类层次结构,则前者是合适的,否则是后者。

抱歉,无法提供更多示例 - 在 iPad 上和出门的路上写这个。

于 2013-01-31T23:06:43.670 回答