Воскресенье, 06 Октября 2024, 17:17

Приветствую Вас Гость

[ Новые сообщения · Игроделы · Правила · Поиск ]
Результаты поиска
nilremДата: Суббота, 31 Октября 2009, 17:15 | Сообщение # 361 | Тема: Игра про Винча!
Просветленный разум
Сейчас нет на сайте
Предупреждаю, если и на этой странице будет столько же флуда или оффтопа, как на первой, тема уедет во флейм.

Windmill 2

WindMill 2D Game Engine
nilremДата: Пятница, 06 Ноября 2009, 14:06 | Сообщение # 362 | Тема: Анимация боя на мечах в максе
Просветленный разум
Сейчас нет на сайте
Quote (ALBAKOR)
это кость, привязанная к ладлни

А что мешает ее отвязать или сделать две кости - одну для меча в ножнах, другую в руке.

Quote (Vinchensoo)
ALBAKOR, сделай отдельно заанимированный меч,

И соответственно анимировать персонально все оружие? А если 50 мечей только внешним видом отличаются?
А затем еще и мучится с синхронизацией анимаций.


Windmill 2

WindMill 2D Game Engine
nilremДата: Пятница, 06 Ноября 2009, 14:12 | Сообщение # 363 | Тема: Ищу работу сценариста для РПГ/Стелялок/стратегий
Просветленный разум
Сейчас нет на сайте
Quote (Девяностых)
А он (Феромон) супер-проф. сценарист\писака, что может взяться за обучение новичков?

Есть такая народная мудрость:
Кто умеет - тот делает, кто не умеет - учит других.)


Windmill 2

WindMill 2D Game Engine
nilremДата: Пятница, 06 Ноября 2009, 14:34 | Сообщение # 364 | Тема: Мобильная ММОРПГ
Просветленный разум
Сейчас нет на сайте
Quote (Vinchensoo)
Или ты думаешь, что умные дядьки не додумались сделать ограбление корованов?)))) Нет, не зря такой игры еще нет

Там можно посмотреть список - Игры, в которых можно грабить корованы


Windmill 2

WindMill 2D Game Engine
nilremДата: Пятница, 06 Ноября 2009, 17:02 | Сообщение # 365 | Тема: Dragon Age: Origins
Просветленный разум
Сейчас нет на сайте
Осталось 0 дней.
Игра вышла!

Взять можно где то в магазинах, но это только для жителей столиц.
На Озоне за 900 р. - http://www.ozon.ru/context/detail/id/4591552/ Правда ждать придется.
Или на http://torrents.ru
Жаль что я сейчас не дома.
Так что, люди, покупаем/качаем, играем и отписываемся здесь.
А я лопну от зависти.


Windmill 2

WindMill 2D Game Engine
nilremДата: Пятница, 06 Ноября 2009, 23:05 | Сообщение # 366 | Тема: Zurg. Заражение
Просветленный разум
Сейчас нет на сайте
В рамках курса "Основы С++ для начинающих программистов игр" делаю небольшую игру. Уроки на ее основе начнутся с шестого, или седьмого(как получится) когда возьмемся за концепции объектно-ориентированного программирования.
Это будет текстовая пошаговая тактическая ролевая игра с псевдографическим интерфейсом. О как завернул.
На данный момент мне нужна помощь в проработке концепции и мира игры.
Ниже приведен тот стержень, на который нужно опираться, подкидывая мне идеи.


Мир игры
Мир игры поделен на планы(этажи), на каждом из которых живут разные существа.
На самом нижнем плане обитают зурги, но об этом никто не знает, поскольку хода туда нет. Как то один обезбашенный техноид Мифлос задался целью соединить все планы Всеобщим тоннелем. Тоннель был построен и им воспользовались не имевшие ранее доступа на верхние планы зурги. Первой их жертвой стал сам Мифлос. Используя доступные ему средства, зурги и смогли быстро заразить большинство нижних планов.

Техническая часть
Уровень(план) представляет из себя квадрат Х на Х клеток. В каждой клетке может находится элемент ландшафта, препятствие вроде куста, дерева, строения, какого-нибудь устройства, и существо( монстр, нпс и тд). Зайдя на клетку с постройкой, в нее можно войти, при этом вместо схемы уровня будет отображаться схема внутренностей помещения.
На каждом зараженном плане находится Корень – место, где формируются споры зургов. Пока корень не уничтожен, монстры на карте будут возрождаться.
Каждый план имеет легальный ход на другие планы(Всеобщий тоннель) который неплохо охраняется, и несколько секретных, которые предстоит найти в процессе приключений.

Сюжет
Вы находитесь на одном из средних планов. Каком, зависит от выбранного класса. Зурги уже прорастили Корень в вашем плане и начали заражение. Сопротивляться им бессмысленно, поэтому приходится покинуть родные места и перебраться выше. Но и там уже началось заражение. Вы решаете хоть как то попытаться противостоять этому.
В первую очередь вам предстоит найти способ сопротивляться воздействию зургов(для каждого класса он свой), а затем додуматься, как уничтожить корень. После чего остается только запечатать тоннель на этом плане и, спустившись ниже, вновь уничтожит корень и так далее. Конечно, на каждом плане должны быть свои заморочки. (На данный момент сюжет кажется мне скудным, а сама концепция планов - скучной. Нужно ее как-то оживить.)

Управление в игре
Поскольку игра текстовая, управление осуществляется введение простых команд, вроде движения, атаки общения и т.д.

Бой
Бой происходит на поле 10х10 клеток. В самом начале на поле только вы и ваш враг. Противники ходят по очереди, перемещаясь, атакуя с дистанции или призывая существ. Для призыва используются, в зависимости от расы:

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

Кроме того обладающие магией могут использовать ее посредством свитков. Свитки покупаются, на использование расходуется мана.
Зурги, при наличии на поле особого юнита(пока не придумал) могут заражать чужих существ.
Для восстановления жизни можно использовать зелья.
В бою юниты ближнего боя перемещаются в указанную точку и атакуют оказавшегося рядом врага(или нескольких в зависимости от способностей). Если рядом по пути следования враг, движение прекращается. Стрелки атакуют дистанционно.
(В общем, система боя похожа на Героев, но во время своего хода нужно задействовать всех юнитов, а не согласно инициативы как в HoMM. Ну и юниты призываются на поле(как в карточных играх), а не все время с предводителем).

Расы

Люди
Стовны – каменные существа.
Нежить – мертвецы.
Зурги – разумная плесень. Заражает и паразитирует на любых живых и оживленных существах. При этом полностью подчиняет их. Способны разрастаться в колонию, принимая вид некого растения. Гигантская колония становится корнем – местом где формируются, дозревают и распространяются споры. Юнит Папоротник – накопитель спор(на самом деле зараженный Нургал).
Фишраны – водяные существа. Обитают только на водных планах, на других почти не встречаются. Наименее уязвимы для зургов. Странно, но разумная плесень почему-то не любит воды.
Нургалы – разумные растения. Вероятно, родственные зургам. Легко поддаются заражению

У каждой расы имеются свои «боевые» существа.
У каждой расы должно быть:
1. Минимум один город на плане.
2. Нечто вроде ратуши.
3. Минимум 5 боевых юнитов.

Классы
Техноид – специалист в механике. Использует различные технические штучки
Владаш – мастер призыва различных существ
Пирос – маг, сокрушающий врагов с помощью огня.
Некрот – некромант по-нашему.
Крушар – воин, полагающийся на силу и большое оружие.
Мечан – искусный воин, специализирующийся на различных мечах и другом небольшом оружии.

Характеристики существ
Жизнь
Скорость – клеток за ход
Мана - энергия для использования свитков
Урон - наносимый урон
Защита - процент и вероятность блокирования урона

Уровня нет, как и опыта. За каждый бой дается несколько кристаллов, которые можно тратить на повышение навыков, характеристик.

Юниты

Механизмы: Манекен, Баллиста, Паровой Молот, Танк, Крупнокалиберная пушка
Нежить: Зомби, Скелет, Вампир, Мара, Лич

Ресурсы
Золото – дается за выполнение заданий, продажу кристаллов силы.
Светной газ – основной источник энергии для механизмов. Хранится в герметичных капсулах. Радиоактивный, может быть причиной мутаций. Производится специальными конденсаторами техноидов. Газ можно купить или получить в качестве вознаграждения. Также можно купить конденсатор, но потом нужно его посещать и забирать выработанный газ.
Кристаллы силы – бонусы за выигранную битву. Используются в основном для повышения навыков и характеристик.

Строения
Город
Поселок
Деревня
Дом
Замок
Шахты Стовнов

Портал
Конденсатор – батарейки для техноидов
Громоотвод – зарядка рун
Фабрика – производство запчастей
Порт – приобретение плавсредств
Башня магов – место торговли свитками и зарядки рун.
Кладбище


Это не все, что пока есть, но думаю хватит.
Чем именно вы мне можете помочь:
• Нужно придумать расы и классы, а также существ, живущих на различных планах.
• В частности нужно юниты зургов. Штук пять.
• Придумать им всем навыки и способности.(два, три на юнит)
• Нужно придумать ресурсы и способы их добычи.
• Механика для боя будет использоваться моя, немного модифицированная(упрощенная) MuRoS, но можете предложить что-то другое.
• А вообще сгодятся любые идеи, даже самые бредовые.

Я сейчас на курсах, так что особой обратной связи не ждите. Ближайшие две недели(это если повезет, а не повезет то до декабря) у меня ограниченный доступ в интернет. Сильно ограниченный. Так что творите, придумывайте.
Пользователи, чьими идеями я воспользуюсь, будут внесены в титры и в благодарность получат от меня +.
Жду ваших предложений.


Windmill 2

WindMill 2D Game Engine
nilremДата: Пятница, 06 Ноября 2009, 23:19 | Сообщение # 367 | Тема: Фэнтезийная РПГ на GameMaker'е
Просветленный разум
Сейчас нет на сайте
Quote (Assasin)
Строго судить не буду, но кидай ГМ и учи какой нить ЯП, так как на ГМ хорошую РПГ не сделаешь.

Но с ГМ у него все же больше шансов, чем учить язык. Да и кто сказал что первая игра должна быть хорошей. Она должна просто быть.

Quote (anisimov)
Карту сделать просто. Воспользуйтесь советом А. Сапковского.

Для создания карт лучше воспользоваться специализированными программами, такими как Fractal_Mapper или Campaign Cartographer.

ЁжеГ,
Так какая конкретно тебе нужна помощь?
Если просто нужны люди то тебе в раздел "Вакансии и работа"


Windmill 2

WindMill 2D Game Engine
nilremДата: Воскресенье, 08 Ноября 2009, 07:43 | Сообщение # 368 | Тема: Бан лог
Просветленный разум
Сейчас нет на сайте
gtasa_love - бан 2 месяца за
обсуждение действий и оскорбление администрации.

Удалил бан Нилрема, накинул еще 2 месяца за оскорбление Алхимика+ создание темы в несоответствующем разделе.
Vinchensoo


Windmill 2

WindMill 2D Game Engine
nilremДата: Среда, 11 Ноября 2009, 09:27 | Сообщение # 369 | Тема: Анимация боя на мечах в максе
Просветленный разум
Сейчас нет на сайте
IHTI
А если один и тот же меч возьмут существа разного роста или телосложения.
Как быть в таком случае?
Делать еще один набор анимаций для каждого варианта?


Windmill 2

WindMill 2D Game Engine
nilremДата: Воскресенье, 15 Ноября 2009, 16:58 | Сообщение # 370 | Тема: Курс : "Основы С++ для начинающих программистов игр."
Просветленный разум
Сейчас нет на сайте
Урок 3. Размер данных и диапазон значений. Модификаторы типа.

Как мы уже знаем байт состоит из восьми битов. Поскольку количество этих самых битов небольшое, то и, естественно, что записать с их помощью много чисел не получится. Поэтому размер переменных увеличивают до нескольких байтов. При этом занимаемое ими в памяти место увеличивается.
Теперь нам необходимо возвратится к уже изученным типам данных и узнать о них то, о чем я раньше умолчал. Сейчас я расскажу, сколько каждый тип данных занимает места в памяти и в каком диапазоне он может принимать значение.
Но прежде чем это делать, нужно объяснить еще один момент. Из школьных уроков математики вы должны знать, что существую положительные и отрицательные числа. В языке С++ такие числа относятся к знаковым, то есть предполагается что перед числом стоит плюс или минус. Ну и поскольку есть знаковые, то можно предположить что есть и беззнаковые, То есть всегда положительные, почему так, поймете дальше. Такие числа имеют отдельное обозначение - unsigned. Это ключевое слово ставится при объявлении переменной перед типом. Знаковые переменные тоже имеют обозначение - signed, но поскольку переменная по умолчанию считается знаковой, то его можно не использовать.
Пример:

Code

unsigned int i;

а запись:

Code

signed int i;

идентична:

Code

int i;

ПРЕДУПРЕЖДЕНИЕ!!!
Применение unsigned к float или double превращает тип переменной в int.

Ключевые слова signed и unsigned называются модификаторы(или спецификаторы) типа. Кроме названных есть еще несколько спецификаторов. Например, ключевые слова short и long которые применяются по отношению к целочисленному типу и увеличивают или уменьшают диапазон значений ну и размер.
Теперь я приведу небольшую таблицу в которой представлены типы даны, занимаемый ими размер и диапазон значений. Стоп. Лучше мы напишем для этого программу. Чтобы это сделать, нам необходимо узнать, как получить размер переменной определенного типа и как вычислить диапазон.
С размером все просто. В языке С++ есть специальная функция, которая умеет это делать – sizeof(). Ну а диапазон нам придется определять вручную, с помощью цикла. Помните, как мы это делали для генератора случайных чисел. Здесь будет нечто подобное, но мы воспользуемся интересным свойством переменных. Допустим, переменная имеет диапазон от 0 до 255. И сейчас ей присвоено максимальное значение 255. Как вы думаете, что будет, если прибавить к нему единицу? Значение переменной изменится на 0. Переменная будто закольцована сама на себя, и за пределы допустимого значения никогда не выйдет. Так и будет изменяться по кругу, в ту или иную сторону. Закольцованность работает в обе стороны, то есть если переменная равна нолю и отнять единицу, то значение станет 255.

В общем вот код:

Code
#include <iostream>

using namespace std;

void main ()
{
  setlocale(0,"");

  bool b;
  char c;
   
  int i=0;
  short int si=0;
  long int li=0;
  unsigned int ui=0;
  unsigned short int usi=0;
  unsigned long int uli=0;
   
  float f=0;
  long float lf=0;
   
  double d=0;  
  long double ld=0;

  cout<<"\tТип данных\t\t"<<"Размер\t"<<"Диапазон значений\n\n";

  cout<<"\t"<<"bool"<<"\t\t\t"<<sizeof(b)<<"\n";
  cout<<"\t"<<"char"<<"\t\t\t"<<sizeof(c)<<"\n";
  int min=0;  
  int max=0;  
  do
  {
   ++i;
   if(i>max)
    max=i;
   else
   {
    if(i<min)
    min=i;
   }
  }
  while(i!=0);
  cout<<"\t"<<"int"<<"\t\t\t"<<sizeof(i)<<"\t"<<min<<" ... "<<max<<"\n";
   
  min=0;
  max=0;
  do
  {
   ++si;
   if(si>max)
    max=si;
   else
   {
    if(si<min)
     min=si;
   }
  }
  while(si!=0);
  cout<<"\t"<<"short int"<<"\t\t"<<sizeof(si)<<"\t"<<min<<" ... "<<max<<"\n";

  long int lmin=0;
  long int lmax=0;
  do
  {
   ++li;
   if(li>lmax)
    lmax=li;
   else
   {
    if(li<lmin)
     lmin=li;
   }
  }
  while(li!=0);
  cout<<"\t"<<"long int"<<"\t\t"<<sizeof(li)<<"\t"<<lmin<<" ... "<<lmax<<"\n";

  unsigned int uimin=0;
  unsigned int uimax=0;
  do
  {
   ++ui;
   if(ui>uimax)
    uimax=ui;
   else
   {
    if(ui<uimin)
     uimin=ui;
   }
  }
  while(ui!=0);
  cout<<"\t"<<"unsigned int"<<"\t\t"<<sizeof(ui)<<"\t"<<uimin<<" ... "<<uimax<<"\n";

  unsigned short int usimin=0;
  unsigned short int usimax=0;
  do
  {
   ++usi;
   if(usi>usimax)
    usimax=usi;
   else
   {
    if(usi<usimin)
     usimin=usi;
   }
  }
  while(usi!=0);
  cout<<"\t"<<"unsigned short int"<<"\t"<<sizeof(usi)<<"\t"<<usimin<<" ... "<<usimax<<"\n";

   unsigned long int ulimin=0;
   unsigned long int ulimax=0;
  do
  {
   ++uli;
   if(uli>ulimax)
    ulimax=uli;
   else
   {
    if(uli<ulimin)
     ulimin=uli;
   }
  }
  while(uli!=0);
  cout<<"\t"<<"unsigned long int"<<"\t"<<sizeof(uli)<<"\t"<<ulimin<<" ... "<<ulimax<<"\n";

  cout<<"\t"<<"float"<<"\t\t\t"<<sizeof(f)<<"\n";
  cout<<"\t"<<"long float"<<"\t\t"<<sizeof(lf)<<"\n";
  cout<<"\t"<<"double"<<"\t\t\t"<<sizeof(d)<<"\n";
  cout<<"\t"<<"long double"<<"\t\t"<<sizeof(ld)<<"\n";

  cin.get();

}

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

Code

  int min=0;  
  int max=0;  
  do
  {
   ++i;
   if(i>max)
    max=i;
   else
   {
    if(i<min)
    min=i;
   }
  }
  while(i!=0);
  cout<<"\t"<<"int"<<"\t\t\t"<<sizeof(i)<<"\t"<<min<<" ... "<<max<<"\n";

Работает он как и в пример с генератором случайных чисел. Цикл начинается с нуля и завершается когда будет пройден круг и переменная вновь станет равна нолю. Тип переменных, в которых хранится минимальное и максимальное значение, должно соответствовать проверяемому типу, иначе оно может туда не поместится.
Ну а в последней строке происходит отображение результата, в ней же с помощью sizeof вычисляется размер переменной в байтах.

Вот такая табличка получится в результате работы нашей программы:



Считать диапазон float и других последующих типов я не стал. Программа и так работает несколько минут, а тронь я float, работала бы несколько дней.
Дело в том, что при одинаковом размере с типом int, float имеет диапазон от -1.0Е+37 до 1.0Е+37.(Если кто не знает что это значит, объясняю. +37 означает что нужно передвинуть точку вправо (от единицы) на тридцать сем знаков, подставив ноли. В результате получится число в котором 38 цифр. (Как его назвать я даже не знаю. Знаю есть квинтиллион, вроде 18 нолей, так ведь и это очень мало) Куда тут int с его 4 миллиардами.
Ладно. Как видно из получившейся у нас таблицы, некоторые типы данных при одинаковом размере отличаются диапазоном значений. В первую очередь это касается знаковых и беззнаковых. Сейчас я объясню, почему так происходит. Если число имеет определенный знак, не важно, плюс это или минус, то естественно, что этот знак должен где-то храниться. В переменной под знак резервируется крайний левый бит, также называемый старшим битом. Если он равен 0 то переменная положительная(знак + ), если 1 то отрицательная(знак -). Потеря всего одного бита уменьшает значение переменной в два раза. Она как бы делится пополам - одна половина для положительных, другая - для отрицательных чисел, что вы и можете видеть.
Упомянув о спецификаторах типа не возможно не рассказать еще об одном – const. Объявленную с его помощью переменную невозможно изменить. Такую переменную называют константой, и используют для хранения определенных постоянных значений. При объявлении константы ей нужно сразу же присваивать значение.
Пример:

Code
const int p = 3,14;

Если где-то далее в коде вы попытаетесь ее изменить, компилятор сразу же сообщит об ошибке. Если вы не присвоите константе значения сразу, а попытаетесь сделать это позже, опять же получите ошибку.
Так делать нельзя:

Code

const int p;
p = 3,14;    // ошибка. Изменить константу нельзя.

Урок 4. Массивы

Сегодняшняя лекция полностью посвящена данным, поэтому сейчас я расскажу еще об одном их типе. Хотя чисто технически это не самостоятельный тип, а способ представления уже известных. Очень часто программы содержат множество, тысячи и миллионы, переменных. Чтобы хоть как-то облегчить жизнь программистам некоторые близкие по смыслу переменные можно объединять в последовательности. Такую последовательность называют массивом.

Одномерный массив

Одномерный массив это список, последовательность близких по смыслу переменных. Как и любые другие типы данных, массив также нужно объявлять. Синтаксис объявления такой:

Тип_данных имя_массива[размер массива];

Здесь:
Тип данных может быть любым известным вам типом. Ограничений нет.
Имя массива также может быть любым, главное чтобы оно соответствовало описанным ранее требованиям к именам.
Размер массива – здесь внутри квадратных скобок указывается, сколько именно переменных будет содержаться в массиве. Каждая такая переменная называется элементом массива.

Теперь, зная синтаксис, объявим массив, состоящий из 10 элементов типа int:

Code
int ar[10];

Доступ к конкретному элементу массива выполняется с помощью его индекса – порядкового номера. Для этого просто указываем индекс в квадратных скобках:

Code
ar[5];

Здесь мы обратились к элементу с индексом пять. Но не к пятому по порядку. Важно запомнить что отсчет в языке программах на языке С++ начинается с 0.
То есть самый первый элемент имеет индекс 0, второй – 1. И так далее. Так что в нашем массиве с 10 переменными, индекс последней переменной будет 9.



Инициализация массива

Свежесозданный массив является пустым. Значение, которое содержат его элементы непредсказуемо и может быть каким угодно в диапазоне объявленного типа данных.
Поскольку пустые массивы, с содержащими мусор элементами, никому не нужны, их нужно как то заполнить. Этот процесс называется инициализацией массива. Инициализировать массив можно несколькими способами.
Например, можно просто присваивать значение каждому элементу.

Code

ar[0] = 10;
ar[3] = 17;

Если всем элементам массива нужно присвоить определенное значении или последовательность значений, то это удобно делать в цикле.
Вот пример кода:

Code
#include <iostream>
using namespace std;

void main()
{
  setlocale(0,"");    // подобная запись устанавливает в консоли язык, используемый в системе по умолчанию.
  int ar[10];   // обьявляем масив
  for(int i=0;i<10;i++)
  {
   ar[i]=0;  // присваиваем значение элементам
  }
   
  for(int i=0;i<10;i++) // цикл, воторый выводит значение елементов
  {
   cout<<"Элемент ar["<<i<<"] = "<<ar[i]<<'\n';
  }
  cin.get();
}

Как он работает, видно из комментариев.

Есть еще один способ инициализации массива. Он используется во время его объявления. Вот синтаксис:

Тип_данных имя_массива[размер массива] = {список_значений};

Code

#include <iostream>
using namespace std;

void main()
{
  setlocale(0,"");  
  int ar[10]={1,2,3,4,5,6,7,8,9,10};    // обьявляем и инициализируем массив  
  for(int i=0;i<10;i++)  
  {
   cout<<"Элемент ar["<<i<<"] = "<<ar[i]<<'\n';
  }
  cin.get();
}

Количество значений в строке {1,2,3,4,5,6,7,8,9,10} желательно, но не обязательно должна соответствовать размеру массива.

Кстати, в целях экономии места подобным способом можно объявить массив, не указывая его размер.

Code
Тип_данных    имя_массива[ ] = {список_значений};

В таком случае размер массива будет равен количеству значений. То есть в результате объявления:

Code
int ar[]={5,7,133};

будет создан массив, содержащий три элемента. Таким способом часто пользуются при объявлении массивов символов, или по-другому строк.

Code
[b]Массив символов[/b]

Строка это одномерный массив, каждый элемент которого относится к типу данных char, а последний символ нулевой (‘\0’) .

image004.png

Объявлять символьный массив можно как любой другой массив, например, так:

Code
char str[7]={'q','w','e','r','t','y','\0'};

Чтобы не заморачиваться с нулевым символом и размером лучше всего использовать следующий способ:

Code
char str[]="qwerty";

здесь размер задается сам, а благодаря использованию кавычек нулевой символ подставляется автоматически. Кстати этот самый нулевой символ используется для того, чтобы программа знала где строка заканчивается.
То есть чтобы при печатании строки

Code
cout << "чтобы программа знала, что остановится нужно после слова здесь";

Я проиллюстрирую это примером:

Code
#include <iostream>
using namespace std;

void main()
{
  setlocale(0,"");  
  char str[]="qwerty";    // обьявляем и инициализируем массив  
  int i=0;
  while(str[i]!='\0')  // пока это не нулевой символ
  {
   cout<<str[i++]<<'\n';
  }
  cin.get();
}

Здесь в цикле печатаются символы, до тех пор, пока не будет достигнут нулевой символ. Приблизительно так и работает cout.

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

Code
#include <iostream>
using namespace std;

void main()
{
  setlocale(0,"");  
  char str[50];    // обьявляем массив  
   
  cin>>str;  // считываем строку
  cout<<"вы ввели: "<<str; //отображение  

  cin.get();
  cin.get();
}

Заметьте, при передаче ввода указывается только имя массива без использования квадратных скобок. То же самое и при выводе с помощью cout. И еще количество введенных символов не должно превышать заявленный размер массива.
Сейчас поясню, почему. Элементы массива размещаются в памяти компьютера друг за другом в смежных ячейках, то есть так, как это показано выше на рисунке. Если количество символов окажется больше, чем размер массива, программа без зазрения совести запишет их последующие ячейки. При это данные, которые были записаны в них ранее будут безвозвратно утеряны. В результате произойдет сбой в работе программы.
Так что будьте внимательны. Создавая массив, заранее просчитывайте, сколько места понадобится.
Описанная здесь проблема касается не только строк, это проблема любых массивов вообще.


Многомерный массив

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

Code
Тип_данных    имя_массива[размер_1] [размер_2]… [размер_N];

Пример:

Code

             float map[10][10]
  int terra[5][50][300];
  char text[100][2][5][500][3][15654][45][10][15];

Мерность массива может быть сколько угодной, но чаще всего используются двухмерные. Их я и буду использовать для примера.
Зачем же могут понадобится многомерные массивы. Примеров на самом деле можно придумать сколько угодно, но я остановлюсь на том, что ближе всего к разработке игр.

Допустим, есть следующий массив:

Code
    int map[5][5];

Вот как его можно представить графически:



На что похоже. Ну, например, на урезанное шахматное поле. Таким способом, сделав массив 8 на 8 элементов можно представить шахматную доску, или если брать шире, то вообще любое игровое поле. А содержащееся значение могут, например, означать силу или количество находящихся там существ, или просто тип юнита.
Инициализируют многомерные массивы также как и обычные. Разве что есть небольшое отличие в записи.

Code
    int map[5][5]=    {{1,2,3,4,5},
      {2,50,43,75,10},  
      {3,44,45,1,9},  
      {4,16,8,5,1},  
      {5,5,8,8,7}};  

Здесь числа собраны в группы по количеству элементов.
Теперь, чтобы лучше понять, что представляют из себя многомерные массивы, напишем небольшую программу. Поскольку урок серьезный, напишем мы таблицу умножения)
Вот код. Он небольшой, так что разобраться сможете:

Code
#include <iostream>
using namespace std;

void main()
{
  setlocale(0,"");  

  int tab[10][10];  //обьявляем массив
  tab[0][0]=0;
  for(int i=1;i<10;i++)
  {
   tab[i][0]=i;  // заполняем верхнюю строку
   tab[0][i]=i;  // заполняем левый крайний столбик
  }

  for(int y=1;y<10;y++)
  {
   for(int x=1;x<10;x++)
   {
    tab[x][y]=x*y;    // заполняем всю таблицу
   }
  }

  for(int y=0;y<10;y++)
  {
   for(int x=0;x<10;x++)
   {
    cout<<tab[x][y]<<'\t';    // печатаем таблицу
   }
   cout<<"\n\n";
  }

  cin.get();

}

Укажу лишь на вложенные цикл:

Code
    for(int y=1;y<10;y++)
  {
   for(int x=1;x<10;x++)
   {
    tab[x][y]=x*y;    // заполняем всю таблицу
   }
  }

Именно так здесь заполняются «коробки, вложенные в коробки».
Завершая с массивами, выполняю данное ранее обещание и привожу код преобразования из десятичной системы в двоичную:

Code
#include <iostream>
using namespace std;

void main()
{
  setlocale(0,"");
  cout<<"введите число от 0 до 255 ";
  int i;
  cin>>i;
  int c[8];    //массив в котором будут хранится биты (1 или 0)
  int ic=7;
  for(int x=0;x<8;x++)
  {
   c[x]=0;    // обнуляем элементы массива
  }
  while(i!=0)    // пока результат деления не равен 0
  {
   c[ic--]=i%2; //присваиваем элементу массива остаток, являющийся битом
   i=i/2;  // делим на 2  
  }
  for(int x=0;x<8;x++)
  {
   cout<< c[x];    //отображаем результат
  }

  cin.get();
  cin.get();
}

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

Code
c[ic--]=i%2;

здесь используется инкремента в постфиксной записи, поэтому сначала вычисляется все выражение и только после переменная ic уменьшается на единицу.

Для преобразования типов существует намного более простой код. Его я приведу, когда мы научимся работать персонально с каждым битом.


Windmill 2

WindMill 2D Game Engine
nilremДата: Воскресенье, 15 Ноября 2009, 17:12 | Сообщение # 371 | Тема: Курс : "Основы С++ для начинающих программистов игр."
Просветленный разум
Сейчас нет на сайте
Урок 5. Манипуляция с битами

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

~ Битовое Не. Меняет значение бита на противоположное. Вообще, если сравнить 1 с логическим true, а ноль с логическим false (как оно в общем-то и есть) здесь можно использовать аналогию с логическими операторами. Помните пример:

Code
!true возвратит false
!false возвратит true

Ну а вот битовый аналог:

Code
~1 (true) возвратит 0 (false)
~0 (false) возвратит 1 (true)

& Битовое И. Как и логический вариант, этот оператор возвращает 1 если оба аргумента равны единице, во всех остальных случаях 0.

Code
1 (true)  & 1 (true)  возвратит 1 (true)
1 (true)  & 0 (false) возвратит 0 (false)
0 (false) & 0 (false) возвратит 0 (false)

Битовый & поочередно применяется ко всем битам числа. Вот как это работает:

11010 & 10101 равно 10000

Немного отформатируем, чтоб было понятнее:
11010 &
10101 равно
10000

Если перевести все в привычную нам десятичную систему, то будет так:

26 & 21 = 16

Вот такая вот арифметика)
Воплотим ее в код:

Code
#include <iostream>
using namespace std;

void main()
{
     cout<< (26 & 21);
     cin.get();
}

| Битовое ИЛИ. И опять знакомая ситуация. Если один из аргументов равен 1, то 1 и будет возвращено .

Code
1 (true)  |  1 (true)  возвратит 1 (true)
1 (true)  |  0 (false) возвратит 1 (true)
0 (false) |  0 (false) возвратит 0 (false)

Пример:
11010 |
10101 равно
11111
26 | 21 = 31

Измените предыдущий пример и убедитесь, что я прав.

^ Исключающее ИЛИ. А вот это нечто новое. Такого мы еще не видели. Оператор ИЛИ возвращает единицу, только если один(только один) из аргументов равен 1.

Code
1 (true)  ^  1 (true)  возвратит 0 (false)
1 (true)  ^  0 (false) возвратит 1 (true)
0 (false) ^ 1 (true)   возвратит 1 (true)
0 (false) ^  0 (false) возвратит 0 (false)

Пример:
11010 ^
10101 равно
01111

В привычном нам, десятичном, виде:

26 ^ 21 = 15

Операторы сдвига

Помимо логических, для работы с битами также используются операторы сдвига. Это уже знакомые нам << и >>. Они сдвигают все биты числа соответственно влево или вправо, заполняя освободившиеся нолем.
Синтаксис использования этих операторов выглядит так:

переменная << число битов
переменная >> число битов

здесь число битов, это значение, означающее на сколько нужно произвести сдвиг.
Пример:

01011010 << 1 даст
10110100

а

01011010 >> 1 даст
00101101

Отлично, сдвигает. Нам то что с того.
Ну например использование комбинация битовых операторов позволяет узнавать или изменять значение каждого бита на свое усмотрение. Для этого придется немного напрячь мозг. Чтобы понять приведенные далее коротенькие примеры необходимо знать то, что я излагал выше.

Проверка бита

Итак, в первую очередь разберемся, как узнать значение конкретного бита переменной v.
Для этого используется вот такое очень простое выражение:

Code
(v & (1 <<n))? Бит = 1: Бит = 0;

Эта запись, если вы помните прошлые уроки, использует условный оператор и может быть записана следующим образом:

Code
if(v & (1 << n))
{
     Бит = 1;
}
else
{
     Бит = 0;
}

Где:
v - переменная,
n - номер бита.

Работает оно так. Сначала с помощью оператора сдвига создается переменная, в которой интересующий нас бит равен 1. Для этого переменная 1 имеющая двоичное представление 00000001, сдвигается на нужное количество битов влево. Допустим нам нужно узнать значение второго бита. Вспомним, что биты нумеруются от ноля.

(1 << 2)
00000001 << 2
Результат сдвига
00000100

Далее применяем битовый И. Если у переменной второй бит 1 то в результате выражения:

Code
    11111111
& 00000100
Получим

00000100  то есть (true)

Если же второй бит равен нолю то:

Code
    11111011
& 00000100
Получим     

00000000  то есть (false)

Ну а дальше в действие вступает условный оператор, который определяет правдивое выражение или ложное и присваивает соответствующую цифру.
Далее я продемонстрирую как с помощью описанного выше способа выполнить преобразование из десятичной системы счисления в двоичную.

Code
#include <iostream>
using namespace std;

void main()
{
     setlocale(0,"");
     cout<<"введите число от 0 до 255 ";
     int i;
     cin>>i;
     for(int n=7;n>=0;n--)
     {
      cout<<(i & (1 << n) ? 1 : 0);
     }
     cin.get();
     cin.get();
}

Намного проще, не правда ли. Если кому-то непонятно, замените строку:

Code
  cout<<(i & (1 << n) ? 1 : 0);

на:

Code
  if(i & (1 << n))
      {
       cout<<1;
      }
      else
      {
       cout<<0;
      }

Теперь дошло?

Установка бита в 1

Проверять биты мы научились. Менять их значение еще проще. Здесь даже условный оператор не нужен. Установка бита в 1 выполняется с помощью такого выражения:

Code
v = (v | (1 << n))

Здесь, как и в прошлом случае используется сдвиг для получения переменной, в которой значение конкретного бита 1, остальных 0.
Далее используется битовый оператор ИЛИ. Если у переменной второй бит и так 1 то в результате выражения:

11111111 | 00000100 Получим 11111111

Если же второй бит равен нолю то:

000000000 | 00000100 Получим 00000100

Далее результат присваивается исходной переменной.

Пример кода:

Code
#include <iostream>
using namespace std;

void main()
{
     setlocale(0,"");
     int i=0;
     cout<<"i = "<<i<<" = ";
     for(int n=7;n>=0;n--)
     {
      cout<<(i & (1 << n) ? 1 : 0);
     }

     i |= (1 << 0);    // устанавливаем нулевой бит в 1
     cout<<"\nустанавливаем нулевой бит в 1\n";
     cout<<"i = "<<i<<" = ";
     for(int n=7;n>=0;n--)
     {
      cout<<(i & (1 << n) ? 1 : 0);
     }

     i |= (1 << 7);    // устанавливаем 7 бит в 1
     cout<<"\nустанавливаем 7 бит в 1\n";
     cout<<"i = "<<i<<" = ";
     for(int n=7;n>=0;n--)
     {
      cout<<(i & (1 << n) ? 1 : 0);
     }

     cin.get();
     cin.get();
}

Здесь, вместо строки

Code
v = (v | (1 << n))

у меня записано

Code
i |= (1 << 0);

Для сокращения записи я воспользовался составным присваиванием |=.

Установка бита в 0

А теперь обнулим бит с помощью:

Code
v &= ~(1 << f)

(Здесь тоже составное присваивание &=)

И вновь используется сдвиг, для получения переменной, в которой значение конкретного бита 1, остальных 0. Далее с помощью битового НЕ значение всех битов меняется на противоположное.

~00000100 дает 11111011

Затем, как и в случае проверки, используется битовый И.
Если у переменной второй бит 1 то в результате выражения:

01010101 & 11111011 Получим 01010001

Если же второй бит уже равен нолю, то там и менять нечего:

10101010 & 11111011 Получим 10101010

Этот код нужно добавить в приведенный выше пример перед cin.get();.

Code
i &= ~(1 << 0);    // обнуляем нулевой бит
     cout<<"\nобнуляем нулевой бит\n";
     cout<<"i = "<<i<<" = ";
     for(int n=7;n>=0;n--)
     {
      cout<<(i & (1 << n) ? 1 : 0);
     }

     i &= ~(1 << 7);    // обнуляем 7 бит
     cout<<"\nобнуляем 7 бит\n";
     cout<<"i = "<<i<<" = ";
     for(int n=7;n>=0;n--)
     {
      cout<<(i & (1 << n) ? 1 : 0);
     }

Вот мы и научились управлять данными на таком тонком уровне как биты. Для полного счастья нам не хватает только добраться до памяти, а конкретно до тех ячеек, в которых вместе со своими битами хранятся переменные. Чем-то подобным мы далее и займемся.

Урок 6. Указатели

На этой лекции я решил подвести черту под типами данных. Поэтому настала очередь рассказать еще об одном – об указателях. Этот тип данных является чем то вроде камня преткновения в языке программирования С++. Благодаря указателям программы приобретают необычайную гибкость и непревзойденную скорость, но благодаря им же появляются многочисленные серьезные, трудно ловимые баги и ошибки.
Итак, указатель это переменная, хранящая в себе адрес другой переменной. Чтобы понять, что я имею ввиду нужно знать, что значит адрес переменной.

Адрес

Как вы уже знаете, память компьютера состоит из битов, которые в свою очередь собраны в байты. У каждого байта имеется свой адрес, представленный в виде шестнадцатеричного числа.
Каждая переменная занимает в памяти от одного до нескольких байтов. Так вот, адресом переменной становится адрес ее первого байта.

Чтобы получить адрес переменной используется оператор взятия адреса - &.
Этот оператор нам уже попадался под видом битового И. Теперь же с его помощью мы будем получать адрес. Действие, которое этот оператор выполняет, зависит от способа его записи. Если он стоит между двумя переменными то это И, если он стоит перед одной переменной то это оператор взятия адреса. Операторы которые применяются всего к одной переменной называются унарными.

Использование указателей.

Как получить адрес, мы уже знаем. Теперь познакомимся с синтаксисом объявления указателя.

Code
тип_данных      *имя_переменной

Здесь ключевую роль играет унарный оператор звездочка *. С помощью которого переменная и становится указателем. Как и при объявлении других переменных, здесь также есть тип данных. Здесь указывается тип данных переменной, на которую ссылается указатель.
Пример

Code
    int i=1564;
     int *pi;    // обьявление указателя
     pi = & i;    // взятие адреса

пример можно сократить

Code
    int i=1564;
     int *pi= & i;    // обьявление указателя и взятие адреса

Здесь в имени указатели, согласно венгерской нотации, используется префикс p. Чтобы не запутаться в своей программе и знать что переменная это указатель, советую вам использовать его всегда.
Теперь напишем небольшую программу, которая выдаст нам адреса нескольких переменных.

Code
#include <iostream>
using namespace std;

void main()
{
      setlocale(0,"");
     int s=50;
     int i=456;
     double d=4.1456;

     int *ps = &s;
     int *pi = &i;
     double *pd = &d;

     cout<<"адрес переменной s "<<ps<<'\n';
     cout<<"адрес переменной i "<<pi<<'\n';
     cout<<"адрес переменной d "<<pd<<'\n';

     cin.get();
     cin.get();

}

Хорошо, объявлять и присваивать адрес указателю мы научились. Но как получить значение, хранящееся по определенному адресу. Для этих целей используется оператор разыменования указателя, и это все та же звездочка *.

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

Code
    cout<<"\nзначение переменной по указателю ps "<<*ps<<'\n';
     cout<<"значение переменной по указателю pi "<<*pi<<'\n';
     cout<<"значение переменной по указателю pd "<<*pd<<'\n';

Все просто.
С помощью разыменования указателя можно не только получить, но и изменить значение переменной. Делается это так:

Code
int i=456;
int *pi = &i;
*pi = 30;

Понять запись можно так. Вначале с помощью звездочки мы обращаемся к значению и присваиваем ей новое. Пример кода будет дальше.
Теперь я приведу пример неосторожного использования указателей. Вот что случится, если случайно, посредством указателей, присвоить переменной с типом int значение с типом double:

Code
#include <iostream>
using namespace std;

void main()
{
      setlocale(0,"");
     int s=50;
     int i=456;
     int a=785;

     cout<<"s "<<s<<'\n';
     cout<<"i "<<i<<'\n';
     cout<<"a "<<a<<'\n';

     double *pd = (double*)&i;
     *pd = 13.4568;

     cout<<"s "<<s<<'\n';
     cout<<"i "<<i<<'\n';
     cout<<"a "<<a<<'\n';

     cin.get();
     cin.get();
}

Здесь
Code
*pd = 13.4568;

Присваивание значения через указатель.
В этой программе есть непонятная нам строка.

Code
    double *pd = (double*)&i;

а конкретно (double*)&i.

Подобная запись называется явным преобразованием типов. Указав при присваивании перед переменной в скобках новый тип, мы получим значение указанного типа.
Пример:

Code
int i = (int)3.14;    // преобразовываем float в int
int i = (int)'a';    // char в int
char c = (char)100; // и наоборот

В некоторых случаях программа может делать преобразования самостоятельно (неявное преобразование). А иногда, как и в примере с указателями, это нужно делать вручную, иначе компилятор выдает ошибку.
Но вернемся к нашему примеру, что же будет в результате присваивания int значения double.
Первое - значение переменной будет некорректным. Второе – при работе программы возникнет ошибка, связанная с порчей памяти, как и в примере с массивами.

Что ж, думаю на этом со знакомством с указателями можно завязывать. Но мы к ним еще не раз вернемся. Позже мы узнаем, зачем же они нужны и сможем раскрыть всю их мощь.

Послесловие

Ну и как обычно пару слов на конец.
Лекция была сложной, и содержала много новой информации. Поэтому вам еще не раз придется к ней возвращаться. Большая часть сведений вам пока не понадобится, разве что для общего понимания. Преобразования систем счисления, манипуляция с битам нужна только в особых случаях. А вот знания про массивы и указатели будут нужны постоянно.
Я надеюсь, что вы прочитали и поняли все, но если что-то пропустили или не совсем поняли, ничего страшного. Можно прочитать и пройтись по примерах еще раз, при этом особое внимание уделяя как раз массивам и указателям.
Ну и если что, спрашивайте.
На этом все, до встречи.

ЗЫ: Я надеюсь, что вас не напугали возникшие сложности, и мы еще встретимся. Ведь так?


Windmill 2

WindMill 2D Game Engine
nilremДата: Воскресенье, 15 Ноября 2009, 17:23 | Сообщение # 372 | Тема: Курс : "Основы С++ для начинающих программистов игр."
Просветленный разум
Сейчас нет на сайте
Лекция 5. Знакомьтесь - Функции.

Здравствуйте.
На третьей лекции вы познакомились с циклами. То есть со способами многократного повторения одного определенного блока кода. Давайте представим себе такую ситуацию. Допустим нам необходимо повторить этот блок кода несколько раз но не в одном месте, а в различных частях программы. Конечно, можно просто написать его несколько раз, более того можно просто этот код скопировать. Но есть способ эффективнее и проще – использовать функции.

Урок 1. Функции

Когда программы становятся очень большими в коде реально можно потеряться. Единственный способ избежать подобных неприятностей, это разделить программу на несколько частей, и работать с ними по отдельности. Этими частями и будут функции. Такой способ программирования называют Процедурным программированием.(Процедура и функция это одно и тоже).
Что же такое функция, по-простому это отдельный блок кода, имеющий определенное назначение, то есть выполняющий какую-то задуманную программистом работу.
Вообще-то с функциями мы уже встречались. Вспомните, когда мы работали с генератором случайных чисел, мы получали системное время с помощью функции time(), устанавливали начальное значение генератора с помощью функции srand(), ну а само число получали с помощью rand().

Использование функции можно разделить на три этапа:
1. Объявление функции
2. Определение функции
3. Вызов.

Объявление функции

Объявление сообщает компилятору о существовании некой функции. Вот синтаксис:

Code

Тип_возвращаемого_значения  имя_функции(список агрументов);

Здесь:
Тип возвращаемого значения – это тип переменной, которую возвращает функция по завершению своей работы. Как я уже сказал, у каждой функции есть свое назначение, она выполняет определенную работу. Возьмем простой пример – функция умеет суммировать два целых числа. Результат этого сложения и будет возвращен в виде целого числа.
Имя функции – это и есть имя. Ведь к функции как то нужно обращаться. К именам функций применимы те же требования что и к именам переменной. Помните главное - имя должно быть понятным.
Список аргументов(еще называют параметры функции) - это записанные в скобках передаваемые функции значения, попросту говоря это список переменных над которыми эта функция будет совершать определенные действия. Объявляются эти аргументы так же как и обычные переменные, и дальнейшем используются внутри функции. Несколько аргументов отделяются друг от друга запятыми. Количество аргументов не ограничено, но им может и не быть вовсе.
И обращаю ваше внимание, что завершает объявление уже знакомые нам точка с запятой.
Пример объявления функций:

Code

void print();

Подобную запись еще называют прототипом функции. Здесь у нас объявлена функция которая не возвращает никакого значения( тип void – пустой тип) и не принимающая ни одного аргумента. Судя из названия, функция умеет что-то там печатать.

Code

int sum(int a, int b);

Это уже немного описанный пример. Функция имеет два аргумента типа int. В результате своей работы она возвращает целочисленное значение (int).
При объявлении в списке аргументов функции имя переменной можно не указывать, то есть достаточно только указать тип данных. Вот так:

Code

int sum(int, int);

Здесь имена a и b отсутствуют, но они в прототипе не очень-то и нужны. Функций мы еще наобъявляемся, так что не будем тратить время и двигаемся дальше.

Определение функции

Итак, функцию мы уже объявили. Компилятор уже знает о ее существовании, но понятия не имеет, как она работает. Весь, принадлежащий функции код это и есть определение, по-другому реализация, функции.
Синтаксис такой:

Code

Тип_возвращаемого_значения  имя_функции(список аргументов)
{
  Блок кода;
}

Здесь мы может видеть уже знакомое нам объявление, но без завершающей точки с запятой. Далее в фигурных скобках идет принадлежащий функции код, то есть определение функции.
По-моему все просто, поэтому перейду сразу к примеру. Ранее мы объявили функцию sum(), сейчас напишем ее определение.

Code

int sum(int a, int b)
{
  return a + b;
}

Аргументы int a, int b используются в функции как обычные переменные.
Как мы уже знаем, функция возвращает определенное значение. Для этих целей в примере мы и видим оператор возврата - return.
Синтаксис оператора следующий:

Code
return возвращаемое значение;

Возвращаемым значением также может быть и любое выражение, как в нашем примере это a + b. Оператор возврата имеет самый низкий приоритет, то есть будет выполнен только после того, как все выражение будет вычислено.
Запомните, тип возвращаемого значения обязательно должен соответствовать типу, указанному в объявлении функции.
В одной функции может содержаться несколько операторов return. Чаще всего в следующей конструкции:

Code

if(условие)
{
  return значение;
}
else  
{
  return значение2;
}

Здесь возвращенное значение зависит от заданного условия.

Вызов функции.

Чтобы воспользоваться функцией, необходимо произвести ее вызов в нужном месте программы. Для вызова используется следующий синтаксис:

Code

имя_функции (значение1, значение2, …)

Здесь количество и тип значений должны соответствовать количеству и типу аргументов, объявленных в прототипе вызываемой функции. То есть, для объявленной ранее функции

Code

int sum(int a, int b);

вызов будет таким:

Code

sum(5, 8);

Даже если функция не имеет аргументов, круглые скобки при ее вызове писать обязательно.
Кроме чисел функции также можно передавать переменные соответствующего типа:

Code

int x=10;
sum(x, 156);

Использование функций.

Ну а теперь напишем небольшую программу, в которой используем все, с чем познакомились ранее.

Code

#include <iostream>
using namespace std;

int sum(int, int);    //обьявление(прототип) функции

void main()
{
  setlocale(0,"");

  int a;
  int a2;
  int b;
  cout<<"Введите первое число ";
  cin>>a;
  cout<<"Введите второе число ";
  cin>>a2;
  b = sum(a,a2);    // вызов функции
  cout<<"Сума двух чисел "<<b;
  cin.get();
  cin.get();
}

int sum(int a, int b)    //определение(реализация) функции
{
  return a + b;
}

В этом примере мы видим все, о чем говорили ранее. Объявление, определение и вызов.
В строке:

Code

b = sum(a,a2);    // вызов функции

возвращенный функцией результат присваивается переменной b.

Пример неплохо иллюстрирует учебный материал, но созданная нами функция бесполезная. Более того, код примера неэффективный и некорректный, поэтому далее, чтобы получше освоить функции и получить еще крупицу опыта, мы его улучшим.
Говоря об эффективности, я имел в виду, что в примере есть бесполезные строки кода, ненужное объявление переменных и другое. От всего этого можно и даже нужно избавится, сделав нашу программу компактнее и быстрее(чисто теоретически, поскольку кода мало и повышение производительности никто не заметит.)
Теперь о корректности. В программе наличествует ввод данных. Подразумевается, что будет ведено какое-то число. Но что если пользователь случайно введет какое-то левое значение?
В программе произойдет сбой. А это не есть хорошо. Поэтому сейчас нам необходимо сделать соответствующую проверку. Поскольку, в случае ввода неверного значения программа должна запросить его повторно и вводить нам нужно несколько значений, получение и проверка данных от пользователя будет производиться внутри одной функции GetDigit(). Вот ее код:

Code

int GetDigit()
{  
  bool r=true;   
  char c[20];
  cout<<"Введите число ";
  while (r)   // основной цикл   
  {
   cin>>c;    // ввод
   for(int i=0;c[i]!='\0';i++)    // цикл анализа
   {
    if(isdigit(c[i]))  // проверка цифра или символ
    {
     r=false;
    }
    else
    {
     r=true;
     cout<<"Повторите ввод (число давай) ";
     break;    // принудительное завершение цикла анализа
    }
   }
  }
int i= atoi(c);
  return i;    // возврат числа
}

Работает она следующим образом. Вначале запрашивается ввод. Далее, внутри основного цикла, введенные данные присваиваются переменной с, являющейся массивом символов. Даже если пользователь введет корректное число, все равно оно будет записано в памяти как набор символов. Это необходимо для того, чтобы:
1. При вводе символа не произошел сбой(при попытке присвоить числу значение, содержащее не цифры а символы.)
2. Чтобы мы могли выполнить анализ.
Далее в еще одном цикле,

Code
for(int i=0;c[i]!='\0';i++)    // цикл анализа
   {
    if(isdigit(c[i]))  // проверка цифра или символ
    {
     r=false;
    }
    else
    {
     r=true;
     cout<<"Повторите ввод (число давай) ";
     break;    // принудительное завершение цикла анализа
    }
   }

до тех пор, пока не встретится конец строки(нулевой символ '\0' ) производится анализ массива. Выполняется проверка каждого элемента на то, является ли он числом или символом. Делается это с помощью стандартной функции isdigit(), возвращающей true если проверяемый символ – цифра. В этом случае анализ продолжается, и если все символы цифры, условие продолжения основного цикла (переменная r) становится ложным.

Если же функция isdigit(), возвращает false, условие продолжения основного цикла вновь становится истинным, выводится предложение повторно ввести число, и цикл анализа принудительно завершается оператором break. Принудительное завершение необходимо потому, что после символа может встретится опять цифра и условие станет ложным, тогда как на самом деле ввод то некорректный.

После завершения основного цикла мы должны возвратить значения.

Поскольку с это массив символов(строка), а нам нужно число, чтобы выполнить соответствующее преобразование, используется еще одна стандартная функция atoi().
(Прототип int atoi(const char *str); Функция принимает указатель на строку, а возвращает число)
Она умеет превращать последовательность символов в число, при условии что символы – цифры. Иначе будет возвращен 0. Здесь нужно быть осторожным, поскольку можно запутаться, ведь строка тоже может быть «0». (Можете сами добавить соответствующую проверку, я этого делать не буду, а ниже приведу более сложный вариант функции GetDigit().)

Code

int i= atoi(c);
  return i;    // возврат числа

здесь с – имя массива, по совместительству являющееся также и указателем.

Эту запись можно сократить, ведь абсолютно незачем использовать лишнюю переменную

Code

  return atoi(c);    // возврат числа

Здесь напрямую возвращается результат работы функции atoi©. То есть, можно сказать, что после выполнения работы функция замещается значением возвращаемого ею типа. Поэтому ее и можно использовать в тексте программы непосредственно как значение:

Code

b = sum(5,10)* sum(5,3);

аналогом этой записи будет

Code
b = 15 * 8;

то есть:

Code
b = (5+10) * (5+3);

Надеюсь это уяснили. Теперь внесем в нашу программу изменения, задействуем созданную нами функцию GetDigit(). Не забудьте добавить ее объявление. Теперь функция main() примет такой вид:

Code

void main()
{
  setlocale(0,"");
  int a, a2;
  a=GetDigit();    // ввод первого числа
  a2=GetDigit();    // ввод второго числа
  cout<<"Сума двух чисел "<< sum(a,a2);    // вызов функции
  cin.get();
  cin.get();
}

Здесь переменные a и a2 получают корректное значение, возвращенное нашей функцией.

ЗАДАЧКА.
До сих пор я не давал вам домашних заданий. Пора это менять. Итак, небольшая задачка. Измените приведенный выше пример так, чтобы переменная а2 оказалась ненужной.

Ну а теперь обещанный мной более продвинутый вариант функции GetDigit().

Code

int GetDigit()
{
  int d;
  cout<<"Введите число ";
  while (true)
  {
   cin >> d;
   if ((cin.peek() == '\n'))
    break;
   else
   {
    cout << "Повторите ввод (ожидается число ) " << "\n";
    cin.clear();
    while (cin.get() != '\n')
    {

}
   }
  }
  return d;
}

Я не буду вам объяснять как она работает. Вы должны попытаться разобраться в ней сами. В этом вам поможет встроенная в студию справка, или если у вас нелады с английским то Интернет.
Основное внимание в этом примере следует уделить новым функциям:
cin.peek()
cin.clear()
cin.get()

Правда с последней вы уже знакомы, но вот что она делает на самом деле вряд ли знаете.

Ах да. Я же на протяжении стольких уроков так и не рассказал, как пользоваться справкой . Виноват. Исправляюсь. Хотя там и рассказывать особо нечего.
Как и во всех других Windows приложениях, справка в студии вызывается нажатием клавиши F1. При этом справка здесь умная. Во вновь открывшемся окне выдается информация об элементе, который в данный момент является активным. Чтобы получить информацию о неизвестной вам стандартной функции языка, достаточно поместить на нее курсор и нажать вышеупомянутую клавишу.

И оп…
Мы получаем всю возможную информацию о данной функции, а также пример ее использования, правда, чаще всего на английском.

Кроме получения информации по функциям в справке содержится еще много чего. Например, можно узнать побольше о какой-нибудь возникшей ошибке. Для этого в списке ошибок выделяем интересующую:

И нажимаем F1. В появившемся окне можно получить информацию не только о природе ошибки, но и подробно, с примерами, узнать причины ее возникновения, а также способы устранения. Кстати если у вас русская студия, то информацию по ошибкам вы получите на родном языке. Эту часть они уже успели перевести.

Порадовал я вас немного рисунками, пора возвращаться к основной теме.
Ну вот вы и научились создавать и использовать функции. Теперь сделаю небольшое дополнение. Функции объявлять не обязательно. Вместо объявления функции можно сразу написать ее реализацию. Вот приводимый ранее пример без объявления.

Code

#include <iostream>
using namespace std;

int sum(int a, int b)    //определение(реализация) функции вместо ее обьявления.
{
  return a + b;
}

void main()
{
  setlocale(0,"");

. . .
  cin.get();
  cin.get();
}

Возникает вопрос, зачем же тогда оно нужно. Это вы сможете понять со следующего урока.

Прикрепления: 5183675.png (10.8 Kb) · 8599656.png (75.7 Kb) · 6298078.png (20.9 Kb)


Windmill 2

WindMill 2D Game Engine
nilremДата: Воскресенье, 15 Ноября 2009, 19:04 | Сообщение # 373 | Тема: Zurg. Заражение
Просветленный разум
Сейчас нет на сайте
Обновил первый пост. Добавил две расы, ресурсы и юниты для двух рас.

Windmill 2

WindMill 2D Game Engine
nilremДата: Воскресенье, 15 Ноября 2009, 19:44 | Сообщение # 374 | Тема: Я удалил DX прогой DirectX Killer. Что делать?
Просветленный разум
Сейчас нет на сайте
Пуск-Все программы-Стандартные-Служебные-Восстановление системы.

Windmill 2

WindMill 2D Game Engine
nilremДата: Воскресенье, 15 Ноября 2009, 23:47 | Сообщение # 375 | Тема: Dead World - [Fps]
Просветленный разум
Сейчас нет на сайте
jekastudio,
Еще раз выложишь такой большой скрин - задушу.
Извинения не помогут.


Windmill 2

WindMill 2D Game Engine
nilremДата: Понедельник, 16 Ноября 2009, 00:10 | Сообщение # 376 | Тема: Karma Studios
Просветленный разум
Сейчас нет на сайте
Завязываем оффтопить.
Тема закрыта.


Windmill 2

WindMill 2D Game Engine
nilremДата: Понедельник, 16 Ноября 2009, 22:09 | Сообщение # 377 | Тема: Новая структура форума.
Просветленный разум
Сейчас нет на сайте
Quote (TLT)
Только мы так и не решили как классифицировать и разделять языки.

Устроить опрос, для популярных языков создать разделы.
Думаю будет нечто вроде:

C++ & Visual C++

Pascal & Delphi

HTML & JS & PHP

Other...

Потом из другого выделять для популярных языков еще разделы


Windmill 2

WindMill 2D Game Engine
nilremДата: Вторник, 17 Ноября 2009, 00:25 | Сообщение # 378 | Тема: Новая структура форума.
Просветленный разум
Сейчас нет на сайте
Gnomov,
Слишком много. Будут пустовать разделы.


Windmill 2

WindMill 2D Game Engine
nilremДата: Вторник, 17 Ноября 2009, 16:51 | Сообщение # 379 | Тема: Сообщество игроделов на GcUp.ru
Просветленный разум
Сейчас нет на сайте
Quote (TrueIfrit)
как ты этого добился?

Могу предположить:
1. Сайт уникальный, других такой тематики и с таким набором контента нет.
2. Имя не юкозовское.


Windmill 2

WindMill 2D Game Engine
nilremДата: Вторник, 17 Ноября 2009, 21:28 | Сообщение # 380 | Тема: Мы набираем команду для разработки большой игры!
Просветленный разум
Сейчас нет на сайте
Хотелось бы услышать больше про саму игру.

Windmill 2

WindMill 2D Game Engine
Поиск:

Все права сохранены. GcUp.ru © 2008-2024 Рейтинг