Ну, я еще немного подумал о таблицах решений в Objective-C и придумал решение для реализации базового. Я не буду публиковать весь код здесь, просто фрагменты, которые делают работу таблицы решений и их основной целью. Я опубликовал это по адресу Code Review SE, если вы хотите увидеть полный код и некоторые полезные советы по его улучшению. Я публикую это сейчас, потому что кто-то отправил комментарий с просьбой о том, чтобы я это сделал, но я, безусловно, в конечном итоге улучшу это и интегрирую предложения из обзора. Во всяком случае, вот код.
Прежде чем использовать метод инициализации, я устанавливаю ряд констант NSString, которые будут использоваться в качестве ключей в NSDictionary.
//Two options for the decision table, either access the dictionary directly with 0-x, the enum values, or make strings for their names
//the advantage of strings is that it is more extensible, and the position in the enum doesnt matter
NSString* const kEnemyMovementStateJustSpawned = @"enemyMovementStateJustSpawned";
NSString* const kEnemyMovementStateIdle = @"enemyMovementStateIdle";
NSString* const kEnemyMovementStateNeedsMoving = @"enemyMovementStateNeedsMoving";
NSString* const kEnemyMovementStateToFloor = @"enemyMovementStateToFloor";
NSString *const kEnemyMovementStateAtDestinationFloor = @"enemyMovementStateAtDestinationFloor";
NSString* const kEnemyMovementStateToFloorExit = @"enemyMovementStateToFloorExit";
NSString* const kEnemyMovementStateToAttackWalls = @"enemyMovementStateToAttackWalls";
NSString* const kEnemyMovementStateToAttackFloor = @"enemyMovementStateToAttackFloor";
NSString* const kEnemyMovementStateToAttackRoom = @"enemyMovementStateToAttackRoom";
Затем я использую эти константы наряду с именами методов в классе, чтобы построить NSDictionary:
-(void) setupDecisionTable {
//the string objects are the names of methods in the class
_decisionTable = @{kEnemyMovementStateJustSpawned: @"doEnemyJustSpawned",
kEnemyMovementStateIdle: @"doEnemyIdle",
kEnemyMovementStateNeedsMoving: @"doEnemyNeedsMoving",
kEnemyMovementStateToFloorExit: @"doFloorMovement",
kEnemyMovementStateToFloor: @"doVerticalMovement",
kEnemyMovementStateAtDestinationFloor: @"doEnemyAtDestinationFloor",
kEnemyMovementStateToAttackWalls: @"doFloorMovement",
kEnemyMovementStateToAttackFloor: @"doFloorMovement",
kEnemyMovementStateToAttackRoom: @"doFloorMovement"
};
}
Тогда каждый тик я называю этот метод, который выполняет метод с именем из объект вытащил из словаря:
-(void) doMovement {
//the selector is formed from a string inside the decision table dictionary
SEL methodToCallName = NSSelectorFromString([_decisionTable objectForKey:[self stringForState:self.state]]);
if (methodToCallName) {
IMP functionPointer = [self methodForSelector:methodToCallName];
void (*methodToCall)(id, SEL) = (void *)functionPointer;
methodToCall(self, methodToCallName);
}
}
-(NSString *) stringForState:(EnemyMovementState)state {
switch (state) {
case EnemyMovementStateJustSpawned:
return kEnemyMovementStateJustSpawned;
case EnemyMovementStateIdle:
return kEnemyMovementStateIdle;
case EnemyMovementStateNeedsMoving:
return kEnemyMovementStateNeedsMoving;
case EnemyMovementStateToFloor:
return kEnemyMovementStateToFloor;
case EnemyMovementStateAtDestinationFloor:
return kEnemyMovementStateAtDestinationFloor;
case EnemyMovementStateToFloorExit:
return kEnemyMovementStateToFloorExit;
case EnemyMovementStateToAttackWalls:
return kEnemyMovementStateToAttackWalls;
case EnemyMovementStateToAttackFloor:
return kEnemyMovementStateToAttackFloor;
case EnemyMovementStateToAttackRoom:
return kEnemyMovementStateToAttackRoom;
default:
return nil;
}
}
Наконец вот несколько методов, которые выполняются, только для законченного примера:
-(void) doEnemyIdle {
if ([self checkFloorsForJobs]) {
self.state = EnemyMovementStateNeedsMoving;
} else {
[self doIdleMovement];
}
}
-(void) doEnemyNeedsMoving {
[self calculateFloorExitPositionByFloor];
self.state = EnemyMovementStateToFloorExit;
}
Это довольно простая реализация. В настоящее время он может обрабатывать только один вход, а лучшая таблица решений может оценивать несколько входных данных и обеспечивать надлежащий вывод. Я думаю, что он может быть расширен путем использования промежуточного метода, который переместил состояние в сочетании с другими переменными, чтобы выбрать подходящий объект из словаря.
После выполнения всего этого, я не уверен, что таблицы решений стоят усилий в Objective-C. Я не знаю, легче ли понимать код, чем оператор switch. Чтобы добавить новую логику в код, ее нужно модифицировать в большем количестве мест, чем требовалось бы сделать оператор switch. Я предоставляю этот код в качестве примера, и было бы здорово видеть другие версии таблиц решений в Objective-C, если у кого-то есть.
Я заметил, что вы отправили некоторый пример кода, связанного с этим вопросом, на [Code Review SE] (http://codereview.stackexchange.com/questions/58292/decision-table-for-the-movement-ai-in- игра). Вы должны [отправить его здесь как ответ] (http://stackoverflow.com/help/self-answer) - это может помочь кому-то другому! – Malcolm