Меню сайта
Форма входа

Категории раздела
Уроки по созданию игр [38]
Программирование игр разной сложности
Игровые алгоритмы [24]
Алгоритмы, которые уже реализованы для разных жанров игр
Графика [5]
Учимся работать с графикой в Делфи
Мультимедиа [3]
Работа с мультимедийными возможностями Делфи
Другие статьи [18]
Статьи не вошедшие не в один из разделов
Ошибки [4]
Всевозможные ошибки и пути их решения
Четверг, 16.05.2024, 16:20
Приветствую Вас Гость

Статьи по программированию

Главная » Статьи » Уроки по созданию игр

Урок 24. Стрельба из лука

Урок 24. Стрельба из лука

 

Начнем с «верха»  с интерфейса ведения стрельбы. Пусть герой можег поражать только врагов, которые находятся только на одной прямой линии с ним - либо по вертикали. Для выстрела по одному из этих четырех направлений удобнее всего использовать клавиши-стрелки, но они уже используются для перемещения персонажа, поэтому за стрельбу по четырем направлениям будут отвечать клавиши a, d, w и z. Процедурой MoveHero будем вызывать новую процедуру HeroShot, которой в ряде параметров зададим единичный вектор направления стрельбы.

 

Case I of

‘a’: HeroShot(-1,0);

‘d’: HeroShot(+1,0);

‘w’: HeroShot(0,-1);

‘s’: HeroShot(0,+1);

End;

 

Разместим в модуле Combat (ссылку на него надо добавить в usеs данного модуля, а заголовок новой процедуры - в интерфейсный раздел Combat):

 

procedure HeroShot(dx,dy: Integer);

begin

end;

 

Далее нам надо определить, как далеко будет лететь пущенная из лука стрела. Это расстояние должно зависеть в первую очередь от самого лука, а во вторую - от ловкости героя. Поэтому условимся, что в структуре TGameltem, описывающей дальнобойное оружие, второй элемент вложенного массива Ints будет хранить число тайлов, на которое лук может послать стрелу:

 

const

intRangedAmmo  =   1;

intRangedRange  =   2;

 

Как правило, это расстояние в подобных играх составляет от 3-5 до 15-25 тайлов. Пусть наш единственный лук стреляет на пять клеток:

 

 (ID:6;

х: 0;   у: 0 ;

IType:itemRangedWeapon;

Name:STR_CROSS;

Ints:    (0,5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

Reals:    (0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0))

 

Как будет проходить процесс выстрела? Надо сымитировать движение стрелы в заданном направлении, и прекратить полет либо успешным поражением объекта, либо неуспешным -если на пути встретилось непроходимое препятствие, или же стрела исчерпала летный ресурс. А предварительно, конечно, надо убедиться, что у героя в руках лук и стрелы. Ну и, как обычно, проверим навык дальнобойной стрельбы.

Однако навыка такого у нас пока нету. Реализацией его мы сейчас и займемся. Добавим в список констант-навыков модуля Него новое значение, и одновременно откорректируем новое максимальное число навыков:

 

const

skillMin = 1;

skillHandWeapon = 1;

skillTrapSearch = 2;

skillDefence = 3;

skillRangedWeapon = 4;

skillMax  =   4;

 

Надо также увеличить MaxSkills в модуле Texts и дополнить соответствующий массив новым названием навыка:

 

MaxSkills   =  4;

SkillsName:   array[1..MaxSkills]   of  string[20]   =

(

'   Ручное   оружие   '    ,    '   Поиск  оружия   '    ,    '   Защита   '    ,    '   Стрельба из  лука'

);

 

Если теперь вызвать компилятор, то выдастся ошибка о неполном массиве соотношений класс-навык - ее мы исправим, например, так:

 

const  ClassSkill_Table:

array[   skillMin..skillMax,   classWarrior..classMage,    1..2   ] of  Integer  =

(

( (80,15) , (50,20) , (25,10) ),

( (50,20) , (80,15) , (60,20) ),

( (50,20) , (50,20) , (15,10) ),

( (50,20) , (80,15) , (35,20) )

);

 

Внести дополнения потребует и массив BaseSkillTable (впрочем, реально этот массив не используется):

 

Const BaseSkill_Table:   array[1..MaxSkills]   of   Integer  =

(

…, 10, 25 30

);

 

Поместим обработку нового навыка в процедуру SkillTest (модуль Него):

 

Case skl of

SkillHandWeapon,

SkillRandedWeapon,

SkillDefence:

  Begin

  succesSkillTest(11,skl);

  end;

 

 

SuccesSkillTest:

Case skl of

SkillRandedWeapon:

Begin

If random(35)=0 then

  Begin

  showInfo(STR_RANGEDWEAPONSKILL_OK);

  Int.H.Skills[skillRangedWeapon];

End;

 

End;

 

Саму константу опишем так:

 

Const RangedWeaponSKILL_OK=   '   Навык стрельбы  повышен!    '    ;

 

Добавим в процедуру HeroShot проверку того, имеется ли в руках у героя нужное оружие (кстати, эту проверку надо сделать первой, до анализа/проверки как в этой процедуре, так и в HeroAttack), и есть ли в этом оружии заряды:


Procedure HeroShot (dx,dy:   Integer) ;
Begin

If Heroes[CurHero].Slots[SLotHands].ITYPE  <>   iteraRangedWeapon then

begin

ShowInfo(STR_NONE_Weapons);

Exit;

End;

 

If Heroes[curHero].Slots[slotHands].Ints[intRangedAmmo]   =   0   then

begin

ShowInfo(STR_NONE_AMMO);

Exit;

End;

If …SkillTest(Heroes[CurHero],   skillRangedWeapon)   then

begin

ShowInfo(STR_BAD_RANGED_ATTACK);

Exit;

end;

 

end;

 

Следующий шаг - снижение числа боеприпасов:

 

dec(Heroes[CurHero].Slots[slotHands].Ints[intRangedAmmo]);

 

Теперь начинается собственно полет стрелы:

 

n   :=  Heroes[CurHero] .Slots[slotHands].Ints[intRangedRange];

x   :=  Heroes[CurHero].x;   у   :=  Heroes[CurHero].у;

while  n   >   0  do

begin

x   :=  x+dx;  

у   :=   y+dy;

if not   FreeTile(   GameMap[CurMap].Cells[x,y].Tile   )    then

Exit;

m   :=   IsMonsterOnTile(x,y);

if m >   0   then

begin

//   атакуем монстра...

MonstersStep;

Exit

end;

dec(n)

end;

 

Обратите внимание, что после выстрела, который попал в монстра, вызывается процедура ответного удара разъяренных тварей MonstersStep.

Сам процесс атаки будет похож на соответствующие действия в ходе ручной схватки. Пока мы не определились, какой будет поражающая способность выстрела. Пусть эта способность определяется луком. Тогда в массиве hits для предметов-луков задействуем еще два элемента, хранящих общеизвестную пару чисел "число кубиков - число граней на каждом кубике:

 

const

intRangedAmmo  =   1;

intRangedRange  =   2;

intRangedDices   =  3;

intRangedDiceNum =   4;

 

Наш единственный лук теперь будет выглядеть так (исправлен массив Ints):


(ID:6; х: 0;   у: 0 ;

IType:itemRangedWeapon;

 Name:STR_CROSS;

Ints:    (0,5, 1,4,0, 0,0,0,0, 0,0, 0,0, 0,0, 0,0,0,0, 0);

Reals:    (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))

 

Бросается один четырехгранный кубик, что не очень много, но не забудем, что стрельба ведется с расстояния, почти без ущерба. С учетем вышесказанного величина поражения будет рассчитываться так:

 

dam   :=  RollDice( Heroes[CurHero].Slots[slotHands].Ints[intRangedDices],

Heroes[CurHero].Slots[slotHands].Ints[intRangedDiceNum]);

 

Следующие действия по использованию этой величины совпадут с действиями из процедуры MeroAttack, поэтому желательно выделить их в отдельный блок:

 

Procedure HeroAttackFin( var H: THero; m, dam: Integer );

Var …:integer;

Begin

…:=RollDice(Monsters[m].dd1, Monsters[m].dd2);

If …=dam then

Begin

ShowInfo(STR_BIG_SKIN);

Exit;

End;

…Monsters[m].HP, dam-skin);

If Monsters[m].HP<=0 then

Begin

…(Monsters[m].Name+STR_MON_KILL);

…Monsters[m].HP);

End;

End;

 

Добавим старый код HeroAttack в конце.

 

…RollDice(3,6);

…Chars[chrSTR] then

…:=dam+dam div 2;

HeroAttackFin(H,m,dam);

MonsterOnTile(x,y);

If … then

RollDice(….Slots[slotHands].ints(intRangedDice),

….Slots[slotHands].ints[intRangedDiceNum]);

HeroAttackFin(Heroes[CurHero],m,dam);

End;

 

Запустим программу и убедимся, что стрельба ведется нормально.

Ограничимся двумя дополнительными возможностями. Во-первых, в зависимости от силы повышать дальность полета стрелы, а во-вторых, в зависимости от удачи будем давать бонус на поражающую способность способом. Сначала попробуем добавить бонус на дальность в 30%:

 

Heroes[curHero].Slots[slotHands].ints[intRangedRange];

…(3,6);

… dam div 2;

 

Расчитаем силу выстрела:

 

RollDice(

Heroes [CurHero] .Slots [slotHands] . Ints"[i3*RangedDice.s] ,

Heroes[CurHero].Slots[slotHands].Ints[intRangedDiceNum]);

d   :=  RollDiceO, 6) ;

if d   <=  Heroes[CurHero].Chars[chrSTR]   then

dam   := dam +   dam div  3;

 

Данный механизм бонусов, конечно, сильно несовершенен - в профессионально сделанных ролевых системах нет такого гладкого изменения способностей. Так, уровень навыка 13 и ниже может вообще никогда не приносить никаких бонусов, а уровень 17 наоборот может внести уже очень существенный вклад. Но мы используем его в основном для наглядности, чтобы программа была работоспособна. Читатель сам может придумать произвольные системы начисления бонусов, главное - как следует сбалансировать игру. Правда, стрелы у нашего героя в ходе тестирования класса "лучник" будут быстро кончаться, поэтому лучше будет все же разместить в пакете со стрелами не 15, как сейчас, а хотя бы 100 стрел:

 

(ID:5; х:0; у:0; IType:itemAmmo;

Name:STR_AMMO;

Ints: (100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);

Reals: (0,0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,0)),

 

А также сотню боеприпасов придать персонажу в момент его генерации (процедура Generatellero):

 

if Heroes[CurHero].Class  =   classRanger   then

begin

Heroes[CurHero].Items[1]    :=   ItemTypes[6];

Heroes[CurHero].Items[1].Ints[intRangedAmmo]    :=   100;

end;

 

Далее - обучаем Героя Магии.

 

Источник: delgame.at.ua

Категория: Уроки по созданию игр | Добавил: Armageddets (27.02.2014) | Автор: Урок 24. Стрельба из лука
Просмотров: 1807 | Теги: программирование игр, делфи, Лазарус, Стрельба из лука, создание рпг игры | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Наш опрос
Где Вам удобнее оставлять коментарии и задавать вопроссы?
Всего ответов: 24
Мини-чат
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0