Comments 24
Вплоть до последнего ждал какого-то неожиданного хэппи-энда. Однако, чудеса бывают редко, а жаль.
+1
Блин, а где ссылка-то, или хотя бы какие то ключевые слова для поиска?))
+2
Это сложный вопрос, мне надо поговорить с женой. Конечно, поеду, я давно мечтал поработать в Париже.
— Но есть нюансы, — сказал босс после короткой паузы.
— А?
— В Париж нельзя.
— Понял, — ответил я. Я ничего не понял.
— Поедешь в Лондон? Там поработаешь немного, и переведем тебя в Париж.
— Мне надо поговорить с женой, — ответил я. – Поеду.
Это шесть баллов по пятибалльной шкале! Серое дождливое утро стало чуть светлее, спасибо!
+3
Похоронить мы решили с выпуском последнего апдейтане выложенное в опенсорс не считается допохороненным.
+2
Отличный рассказ!
А почему не упомянули про разработку AI в движке — там тоже порядочно интересного должно было быть!
Текущее поведение правда немного напоминает пресловутых крисалидов…
P.S. И что за главная тема играет во время сражения? Тоже работа фрилансеров?
Очень напоминает по атмосферности что-то в духе первого Deus Ex.
+1
Спасибо.
Действительно, совершенно забыл рассказать про AI. Правда, ничего особо интересного в нем нет, просто машина состояний. Единая для всех врагов. Враг перед ходом оценивает свое здоровье, сколько его сторонников рядом и сколько противников рядом, и либо атакует, либо бежит к ближайшему стороннику.
Высокоуровневые противники могу отправлять сообщение на сбор всех на подмогу, тогда низкоуровневые бросают все и бегут к высокоуровневому.
В общем, все довольно примитивно, и было написано довольно быстро.
Только листинг функции очень обьемный ))
По музыке, к сожалению, не подскажу, ей занимались Федор Михалыч и Димон.
Действительно, совершенно забыл рассказать про AI. Правда, ничего особо интересного в нем нет, просто машина состояний. Единая для всех врагов. Враг перед ходом оценивает свое здоровье, сколько его сторонников рядом и сколько противников рядом, и либо атакует, либо бежит к ближайшему стороннику.
Высокоуровневые противники могу отправлять сообщение на сбор всех на подмогу, тогда низкоуровневые бросают все и бегут к высокоуровневому.
В общем, все довольно примитивно, и было написано довольно быстро.
Только листинг функции очень обьемный ))
По музыке, к сожалению, не подскажу, ей занимались Федор Михалыч и Димон.
Большой и страшненький кусок кода
void agProcessAI() {
int friendsnear;//, foesnear; - self.visibleenemiescount
int closestfoe, closestfriend, closestvisiblefoe;
int closestfoedistance, closestfrienddistance, closestvisiblefoedistance;
int proceedfalg;
int currmodelnum;
int unitsAlive = 0;
// if there are animations, we do not proceed
// we check if there are any active animations, like running or shooting
proceedfalg = LEAF_YES;
for (int i=0; i<agVars.enemyUnitsCount; i++) {
// wait for ongoing animations to finish
if(
(agVars.enemyUnits[i].model.currAnimation != ANIMSTAND) &&
(agVars.enemyUnits[i].model.currAnimation != ANIMSIT) &&
(agVars.enemyUnits[i].model.currAnimation != ANIMDEAD)
)
{
proceedfalg = LEAF_NO;
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: agProcessAI: waiting for enemyUnit[%i] to finish animation %i\n", i, agVars.enemyUnits[i].model.currAnimation);
#endif
}
if(agVars.enemyUnits[i].health > 0) {
unitsAlive++;
}
}
// if there are animations, finish method.
if(!proceedfalg) {
return;
}
// and if there are no active animations, we are free to act next
// do some thinking
switch (agVars.aiState) {
case LEAFAIGLOBALSTATEINIT:
// we need to reset any AI info left from previous turn
for(int i=0; i<agVars.enemyUnitsCount; i++) {
//agVars.enemyUnits[i].aiInbox = AIMESSAGENOMESSAGES;
agVars.enemyUnits[i].aiState = LEAFAIACTIONINIT;
agGetVisibilityFor(&agVars.enemyUnits[i], agVars.playerUnits, agVars.playerUnitsCount);
}
agVars.aiState = LEAFAIGLOBALACTION;
//break; // GLOBAL AI INIT - no need to break, we are ok to proceed with ACTION
case LEAFAIGLOBALACTION:
// main part. We check every model's AI state and do something
// find first model to work on
currmodelnum = -1;
for(int i=0; i<agVars.enemyUnitsCount; i++) {
if(agVars.enemyUnits[i].aiState != LEAFAIACTIONENDMOVE) {
currmodelnum = i;
break;
}
}
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: ******\nprocessing enemy %i\n*******\n", currmodelnum);
#endif
if(currmodelnum < 0) {// if there are no models to work on, set turn completion flag and return
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: Done all, finishing\n");
#endif
agVars.aiState = LEAFAIGLOBALCOMPLETED;
return;
}
// check if we are alive
if((agVars.enemyUnits[currmodelnum].health > 0) &&
(agVars.enemyUnits[currmodelnum].TUs > 0) &&
(agVars.enemyUnits[currmodelnum].aiState != LEAFAIACTIONENDMOVE)) // alive, has TUs and needs processing
{
// GET some location info...
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: desciding what to do for enemy %i\n", currmodelnum);
#endif
// first we findout how many friends and foes do we see
friendsnear = 0;
//foesnear = 0;
agVars.enemyUnits[currmodelnum].visibleEnemiesCount = 0;
closestfriend = -1;
closestfoe = -1;
closestvisiblefoe = -1;
closestfoedistance = GAMEVIEWDISTSQARED*GAMEVIEWDISTSQARED;
closestfrienddistance = GAMEVIEWDISTSQARED*GAMEVIEWDISTSQARED;
closestvisiblefoedistance = GAMEVIEWDISTSQARED*GAMEVIEWDISTSQARED;
for(int ii=0; ii<agVars.enemyUnitsCount; ii++) {
if((agVars.enemyUnits[ii].health > 0) && (ii != currmodelnum)) { // if alive and not self
int distance = (int)agGetDistanceSQ(&agVars.enemyUnits[currmodelnum], &agVars.enemyUnits[ii]);
if(distance < closestfrienddistance) {
// this is closest friend so far
closestfrienddistance = distance;
closestfriend = ii;
}
if(distance < GAMEVIEWDISTSQARED) {
friendsnear++;
}
}// if alive
}// for enemycount
for(int ii=0; ii<agVars.playerUnitsCount; ii++) {
if(agVars.playerUnits[ii].health > 0) {
int distance = (int)agGetDistanceSQ(&agVars.enemyUnits[currmodelnum], &agVars.playerUnits[ii]);
if(distance < closestfoedistance) {
// this is closest foe so far
closestfoedistance = distance;
closestfoe = ii;
// can we see this foe?
if(agIsVisibleFor(&agVars.enemyUnits[currmodelnum], &agVars.playerUnits[ii])) {
agVars.enemyUnits[currmodelnum].visibleEnemiesCount++;
if(distance < closestvisiblefoedistance) {
closestvisiblefoedistance = distance;
closestvisiblefoe = ii;
}
}
}
/*
if(distance < GAMEVIEWDISTSQARED) {
agVars.enemyUnits[currmodelnum].visibleEnemiesCount++;
}
*/
}// if alive
}// for modelcount
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: see %i friends and %i foes\n", friendsnear, agVars.enemyUnits[currmodelnum].visibleEnemiesCount);
#endif
// what state are we in?
switch (agVars.enemyUnits[currmodelnum].aiState) {
case LEAFAIACTIONINIT:
// what to do?
//************************************************************************************
// check messages
if(agVars.enemyUnits[currmodelnum].aiInbox != AIMESSAGENOMESSAGES) {
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONCHECKMESSAGES;
return;
}
// call to arms?
if( (agVars.enemyUnits[currmodelnum].visibleEnemiesCount) && (enemy[agVars.enemyUnits[currmodelnum].type].callToArmsDistanceSQ > SMALLNUMBER) ) {
for(int i=0; i<agVars.enemyUnitsCount; i++) {
if(i == currmodelnum) {
continue;
}
// check if a callee is close enough
if(agGetDistanceSQ(&agVars.enemyUnits[currmodelnum], &agVars.enemyUnits[i]) < enemy[agVars.enemyUnits[currmodelnum].type].callToArmsDistanceSQ) {
agVars.enemyUnits[i].aiInbox = AIMESSAGECALLTOARMS;
agVars.enemyUnits[i].aiSender = currmodelnum;
#ifdef DEBUGACTIONGAME
agPrintToInfoboard(getLocalizedString("DE7"));
#endif
}
}
}
//************************************************************************************
// am I the last man standing?
if(unitsAlive < 2) {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: I am the last man standing.... BERSERK\n");
agPrintToInfoboard(getLocalizedString("DE8"));
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIBERSERK;
return;
}
// if there are no foes near...
if(agVars.enemyUnits[currmodelnum].visibleEnemiesCount == 0) {
// if the closest enemy is very far, do nothing
if(closestfoedistance > (int)((float)GAMEVIEWDISTSQARED*GAMEPERCEPTIONDISTANCE)) {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: enemies far, do nothing\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONDONOTHING;
return;
} else { // closest enemy is close enough to think...
// if there is more then 1 friend arround, and we have TUs to FIRE, we try to come closer to the enemy
if( (friendsnear > 1) && (agVars.enemyUnits[currmodelnum].TUs > TUCOSTFIRE) ) {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: enemies far, but friends arround. I'll try to come closer to my closest enemy\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONCOMECLOSER;
return;
} else {// if there are no friends arround, or we do not have enough TUs to fire
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: enemies far, no friends arround, i'll stay here\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONDONOTHING;
return;
}
}// enemies close enough but unseen
}// no foes near
else { // there are foes near
// and there are friends arround
if(friendsnear > 0) {
// if we are OK on health, come closer and attack
if(agVars.enemyUnits[currmodelnum].health > AIHEALTHLOW) {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: i have a lot of health, i'll come closer and attack\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONCOMEANDFIRE;
return;
} else { // health low, run away
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: health low, running away\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONRUNAWAY;
return;
}
}// foes near, friends near
else { //no friends near
// if there are more then 2 foes and no friends, run away
if(/*foesnear*/agVars.enemyUnits[currmodelnum].visibleEnemiesCount > 2) {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: no friends arround, too many foes, running away\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONRUNAWAY;
return;
} else { // less then 2 foes and no friends
// if there is enough health, attack
if(agVars.enemyUnits[currmodelnum].health > AIHEALTHLOW) {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: no friends, less then 2 foes, but health ok, attacking\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONFIRENOW;
return;
} else { // health problems, run away
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: no friends, less then 2 foes, low health -> running away\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONRUNAWAY;
return;
}
}// no friends and less then 2 foes
}// foes near, no friends near
}// foes near
break; // LEAFAIACTIONINIT
case LEAFAIACTIONFIRENOW:
// is enemy visible?
if(!agIsGloballyVisible((int)agVars.enemyUnits[currmodelnum].x, (int)agVars.enemyUnits[currmodelnum].z)) {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: i see enemies, but i am invisible, i will do nothing\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONDONOTHING;
return;
}
// if we can, fastsit!
if((agVars.enemyUnits[currmodelnum].skills & skills[SKILLFASTSIT]) && (!agVars.enemyUnits[currmodelnum].isSitting))
{
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: Can sit!\n");
#endif
agVars.enemyUnits[currmodelnum].isSitting = LEAF_YES;
md2ChangeAnimation(&agVars.enemyUnits[currmodelnum].model, ANIMSITDN, agVars.currTime);
return;
}
// before firing, we need to make sure there are visible foes, and shoot them only!
if(closestvisiblefoe >= 0) {
agAIFire(&agVars.enemyUnits[currmodelnum], &agVars.playerUnits[closestvisiblefoe]);
} else {
// there are no visible foes, what do we do????
// we're either already came closer, or we cannot...
// so we pass
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONDONOTHING;
}
break;// LEAFAIACTIONEFIRENOW
case LEAFAIBERSERK:
// this happens when there's only one enemyunit left on the map.
// right now it will act as if it recieved LEAFAIACTIONCOMEANDFIRE
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONCOMEANDFIRE;
break; // LEAFAIBERSERK
case LEAFAIACTIONCOMEANDFIRE:
agAIComeCloser(&agVars.enemyUnits[currmodelnum], (int)agVars.playerUnits[closestfoe].x, (int)agVars.playerUnits[closestfoe].z, TUCOSTFIRE, LEAFAIACTIONINIT);
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONFIRENOW;
break;// LEAFAIACTIONCOMEANDFIRE
case LEAFAIACTIONCOMECLOSER:
agVars.enemyUnits[currmodelnum].aiState = agAIComeCloser(&agVars.enemyUnits[currmodelnum], (int)agVars.playerUnits[closestfoe].x, (int)agVars.playerUnits[closestfoe].z, TUCOSTFIRE, LEAFAIACTIONINIT);
break;// LEAFAIACTIONCOMECLOSER
case LEAFAIACTIONRUNAWAY:
// same as walk closer, but walking closer to nearest friend
// if there are any TUs, we run!
if(agVars.enemyUnits[currmodelnum].TUs > 0) {
// will run away if there is a friend to run to. If left alone, fight!
if(closestfriend < 0) {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: Want to run away, but no friends near.\n");
#endif
if(agVars.enemyUnits[currmodelnum].TUs >= TUCOSTFIRE) {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: No friends to run to but have TUs to fire!\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONFIRENOW;
return;
}// have TUs to fire
else {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: No friends to run to and no TUs to fire.. Staying here.\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONDONOTHING;
return;
}// no TUs to fire
}// there are no friends close
agVars.enemyUnits[currmodelnum].aiState = agAIComeCloser(&agVars.enemyUnits[currmodelnum], (int)agVars.enemyUnits[closestfriend].x, (int)agVars.enemyUnits[closestfriend].z, TUCOSTAUTOFIRE, LEAFAIACTIONFIRENOW);
}
break;// LEAFAIACTIONRUNAWAY
case LEAFAIACTIONDONOTHING:
// if unit has FASTSIT skill, we sit it down
if(agVars.enemyUnits[currmodelnum].health > 0) {
if((agVars.enemyUnits[currmodelnum].skills & skills[SKILLFASTSIT]) && (!agVars.enemyUnits[currmodelnum].isSitting) )
{
agVars.enemyUnits[currmodelnum].isSitting = LEAF_YES;
md2ChangeAnimation(&agVars.enemyUnits[currmodelnum].model, ANIMSITDN, agVars.currTime);
break;
} else {
// now it has nothing to do and sits, if it has fastsit
// we check for any RELEASED hostages we can kill and shoot them, if we can.
if(agVars.enemyUnits[currmodelnum].TUs >= TUCOSTFIRE) {
for(int k=0; k<agVars.hostageUnitsCount; k++) {
if( (agVars.hostageUnits[k].health > 0) && (agVars.hostageUnits[k].aiState == LEAFAIHOSTAGERELEASED) )
{
if(agIsVisibleFor(&agVars.enemyUnits[currmodelnum], &agVars.hostageUnits[k])) {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: Nothing to do, but i see a released hostage to kill.\n");
#endif
agAIFire(&agVars.enemyUnits[currmodelnum], &agVars.hostageUnits[k]);
agVars.enemyUnits[currmodelnum].TUs -= TUCOSTFIRE;
}
}
}
}
}
}
// Then we are done
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONENDMOVE;
break;
//************************************************************************************
case LEAFAIACTIONCHECKMESSAGES: {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: in LEAFAIACTIONCHECKMESSAGES\n");
#endif
switch(agVars.enemyUnits[currmodelnum].aiInbox) {
case AIMESSAGECALLTOARMS:
// FIRST OF ALL!!! clear message
agVars.enemyUnits[currmodelnum].aiInbox = AIMESSAGENOMESSAGES;
// go to sender
if(agVars.enemyUnits[currmodelnum].TUs > 0) {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: recieved CALLTOARMS from %i, going to: %i:%i to %i:%i\n", agVars.enemyUnits[currmodelnum].aiSender, (int)agVars.enemyUnits[currmodelnum].x, (int)agVars.enemyUnits[currmodelnum].z, (int)agVars.enemyUnits[agVars.enemyUnits[currmodelnum].aiSender].x, (int)agVars.enemyUnits[agVars.enemyUnits[currmodelnum].aiSender].z);
#endif
// if sender is close enough, ignore
if(agGetDistanceSQ(&agVars.enemyUnits[agVars.enemyUnits[currmodelnum].aiSender], &agVars.enemyUnits[currmodelnum]) <= 9)
{
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: I am too close to come closer to sender. On my own now.\n");
#endif
// reinit AI.
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONINIT;
return;
}
agVars.enemyUnits[currmodelnum].aiState = agAIComeCloser(&agVars.enemyUnits[currmodelnum], (int)agVars.enemyUnits[agVars.enemyUnits[currmodelnum].aiSender].x, (int)agVars.enemyUnits[agVars.enemyUnits[currmodelnum].aiSender].z, TUCOSTFIRE, LEAFAIACTIONINIT);
}// enough TUs to come closer
else { // no enough TUs to come closer
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: No TUs to come closer. I'll do nothing\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONDONOTHING;
return;
}
break;// AIMESSAGECALLTOARMS
default:
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: Unknown message!!!\n");
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONINIT;
break;
}
} break;// LEAFAIACTIONCHECKMESSAGES
//************************************************************************************
default:
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: Unknow AI state in EnemyLogic for enemy %i\n", currmodelnum);
#endif
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONENDMOVE;
return;
break;
}// switch currmodel->AIstate
}// alive and has TUs
else {// dead and/or has no TUs
agVars.enemyUnits[currmodelnum].aiState = LEAFAIACTIONENDMOVE;
return;
}// dead and/or has no TUs
break; // GLOBAL AI ACTION
case LEAFAIGLOBALCOMPLETED: {// finish turn
// but first...
// we will check if there are units who have a lot of unused TUs
// if there are other units not far who see enemies, we move those units to support them
int noFurtherMoves = LEAF_YES;
for(int i=0; i<agVars.enemyUnitsCount; i++) {
if(agVars.enemyUnits[i].health < AGAIHEALTHTOHELP) {
continue;
}
if(enemy[agVars.enemyUnits[i].type].helpDistanceSQ < SMALLNUMBER) { // 0.0f for units who don't run to help
continue;
}
if(agVars.enemyUnits[i].TUs > AGAITUSTOMOVETOHELP) {
// enough TUs. Let's see if there are units around who see eneies
float closestUnitSQ = GAMEVIEWDISTSQARED*2;// too far to start with
int helpeeIndex = -1;
for(int k=0; k<agVars.enemyUnitsCount; k++) {
// if self, skip
if(i==k) {
continue;
}
// is the new unit seeing enemy
if(agVars.enemyUnits[k].visibleEnemiesCount > 0) {
// is unit close enough? is unit far enough? (Maybe we're already there)
float newDistance = agGetDistanceSQ(&agVars.enemyUnits[i], &agVars.enemyUnits[k]);
if((newDistance < enemy[agVars.enemyUnits[i].type].helpDistanceSQ) && (newDistance > 1.0f-SMALLNUMBER)) {
// do we already know a unit who needs help and is closer
if( (helpeeIndex == -1) || (closestUnitSQ > newDistance) ) {
helpeeIndex = k;
closestUnitSQ = newDistance;
}// found new helpee
}// found close helpee
}// found helpee
} // for all helpees
if(helpeeIndex > -1) {
#ifdef DEBUGACTIONGAME
leafLog(LEAF_NO, "AG: AI: enemy %i is rushing to help enemy %i\n", i, helpeeIndex);
agPrintToInfoboard(getLocalizedString("DE6"));
#endif
agVars.enemyUnits[i].aiState = agAIComeCloser(&agVars.enemyUnits[i], (int)agVars.enemyUnits[helpeeIndex].x, (int)agVars.enemyUnits[helpeeIndex].z, TUCOSTAUTOFIRE, LEAFAIACTIONINIT);
// is there a path at all?
if(agVars.enemyUnits[i].aiState != LEAFAIACTIONDONOTHING) {
noFurtherMoves = LEAF_NO;
agVars.aiState = LEAFAIGLOBALACTION;
break;
}
}
}// if helper has enough TUs
}// for all helpers
if(noFurtherMoves) {
agChangeTurn(TURNHOSTAGES);
}
} break; // GLOBAL AI COMPLETED
default:
break;
}// switch global AIstate
}// agProcessAI
0
Прекрасный захватывающий рассказ.
+1
Очень крутая трилогия вышла. Даже жаль что нет смарта от эпл, с радостью бы поиграл. Спасибо за рассказ.)
+1
Спасибо за текст. Замечательно написано :-)
Имхо основное правило начинающих гейм девелоперов — не писать свой движок. Их же полно готовых уже, а так пару лет вы потеряли на это. И вообще, современные игры это на 30% красивый контент, на 30% раскрутка и купленный трафик, и лишь на 30% код. Ваш результат в плане прибыли тут увы, закономерен.
Но зато получили интересный опыт, а это классно. И респект за терпение — делать проект 3 года сможет не каждый.
Имхо основное правило начинающих гейм девелоперов — не писать свой движок. Их же полно готовых уже, а так пару лет вы потеряли на это. И вообще, современные игры это на 30% красивый контент, на 30% раскрутка и купленный трафик, и лишь на 30% код. Ваш результат в плане прибыли тут увы, закономерен.
Но зато получили интересный опыт, а это классно. И респект за терпение — делать проект 3 года сможет не каждый.
+1
Мне было интересно писать именно движок. Я по натуре не игродел, наверное )
Но, думаю, вы правы.
Но, думаю, вы правы.
+1
Перед тем как начать, стоило хотя бы перечитать статьи на gamedev.ru.
Я тоже делал раз простую игру для iOS, в итоге понял что бесполезно — сейчас gamedev это миллионный рынок где рулит бабло, а бюджеты современных игр уже, бывает, превосходят бюджеты художественных фильмов. Эпоха когда можно было написать «Тетрис» или «Поле чудес» и стать знаменитым, увы прошла.
Про тот же XCom, в плане понимания масштаба и человеко-часов:
www.eurogamer.net/articles/2012-03-06-firaxis-xcom-is-a-very-very-big-budget-game
«We're 50, 60 guys, I don't know exactly,» he said. «We've been working on it for three-and-a-half, four years. It's a big, big game. It's definitely as big as any game we've ever made at Firaxis. It's huge. It's a bit like piloting a big old boat.»
Вообще, gamedev это своя специфика разработки — сжатые сроки и частые переработки, меняющиеся требования, зажранные юзеры, невысокие зарплаты, и эта ниша для фанатов в общем-то. Я год проработал, в итоге понял что это не мое от слова «совсем».
Удачи в Париже, и в изучении французского. Был там этим летом :)
Я тоже делал раз простую игру для iOS, в итоге понял что бесполезно — сейчас gamedev это миллионный рынок где рулит бабло, а бюджеты современных игр уже, бывает, превосходят бюджеты художественных фильмов. Эпоха когда можно было написать «Тетрис» или «Поле чудес» и стать знаменитым, увы прошла.
Про тот же XCom, в плане понимания масштаба и человеко-часов:
www.eurogamer.net/articles/2012-03-06-firaxis-xcom-is-a-very-very-big-budget-game
«We're 50, 60 guys, I don't know exactly,» he said. «We've been working on it for three-and-a-half, four years. It's a big, big game. It's definitely as big as any game we've ever made at Firaxis. It's huge. It's a bit like piloting a big old boat.»
Вообще, gamedev это своя специфика разработки — сжатые сроки и частые переработки, меняющиеся требования, зажранные юзеры, невысокие зарплаты, и эта ниша для фанатов в общем-то. Я год проработал, в итоге понял что это не мое от слова «совсем».
Удачи в Париже, и в изучении французского. Был там этим летом :)
+1
А почему вы решили делать свой движок, а не использовать тот же Unity, например?
0
Как-то грустно, похоронили со всеми почестями?
+1
Sign up to leave a comment.
Своя игра. Часть 3