Избавляемся от однообразия в играх

govnoproger.ru > as3 > Избавляемся от однообразия в играх

Не совсем конечно по теме as3, но тк геймдев, то можно в принципе и написать.

Речь пойдёт о крупных Играх с большой буквы этого слова.

Как пример скучного и тупого однообразия я буду использовать игру «Корсары 3» неважно какую часть. Ну пускай например будет город потерянных кораблей.

Её я буду сравнивать с великими типа диабло 2, Блэйд оф даркнесс, и конечно-же нашей любимой халфой — первой и второй частями.

Всё это великие игры, действительно крутые и про них не слышал только дибил.

Крутизна этих игр именно в проработке, но конечно же в них есть баги и недостатки…

Корсары 3 — это сама по себе потрясающе убогая игра, с потрясающим однообразием.

Дело в том, что её можно было сделать раз в 500 лучше. В этой заметке пойдёт речь именно об этом.

Давайте используя теоретические знания слегка её улучшим. Также корсары 3, я буду сравнивать с первой частью «Корсары – проклятие дальних морей» .

Я не буду описывать баги игры Корсары 3, если бы я это сделал — тут был бы список в километр.

Со времён первый части корсаров «Проклятие дальних морей» , никаких улучшений в игре нету.

Вторая часть «Пираты карибского моря» внесла немного разнообразия, но она по сути также сделала игру хуже.

Один человек когда-то написал следующую фразу:

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

В принципе он прав, но в том случае если речь идёт о какой-то мелкой игрушке.

Если игра крупная, то нужно работать над её недостатками. Разработчики корсаров 3 отнеслись к игре как к какой-то мелкой поделке.

Фехтование

В корсарах 3, фехтование хуже чем в первой части. В первой части вы всегда сражались с капитаном, где количество его и ваших жизней — это размер вашей команды. Сила удара зависит от этого + немного добавляет умение фехтовании.

Система фехтования в первой части:

  • Удар слева
  • Удар справа
  • Блок слева
  • Блок справа
  • Финт слева
  • Финт справа
  • Отсутствие лечения

В третьей части фехтование происходит так:

Изначально бой на палубе. Ваш капитан в нём участвует, а вражеский нет.

Генерируется определённое количество человечков на вашей стороне и на стороне противника.

Если вы проигрываете бой на палубе — вы проигрываете абордаж.

Система фехтования 3 части:

  • удар простой
  • удар пробивающий
  • удар — выпад
  • круговой удар
  • парирование
  • финт
  • блок
  • мушкет
  • лечение

Начнём с того, что когда у противника закончится энергия — то в блоке можно сидеть бесконечно.

Неважно в спину вас бьют или нет.

Если вы выиграете битву на палубе, то происходит бой с капитаном уже в каюте, если вы его проиграете, вы проигрываете абордаж.

Итого: наш герой должен быть крутым воином с кучей хит поинтов.

Сравним боевую систему с игрой Blade of Darkness:

Комбо, как и простые удары, зависят от комбинации кнопок.

Например:

атака + влево = удар слева

(Я все не буду описывать)

Блок — щитом можно зажать бесконечно пока щит не расколется. Блочит только сам щит, спина уязвима и тп.

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

Лечение — при нажатии герой выпивает фуфырик — если противник его ударит в это время, то фуфырик упадёт и жизней не добавится.

Итак вы видите насколько новая игра проигрывает старым.

Считаю что вражеский кэп должен участвовать в битве на палубе иначе это просто не честно.

Абордажные сцены и корабли

Корсары 3 - абордажная сцена мелкого корабля

абордажная сцена мелкого корабля

Корсары 3 - абордажная сцена большого корабля

абордажная сцена большого корабля

Взяв кораблей 50 на абордаж, игрок начинает замечать что все абордажные сцены одни и те-же…

Сцены в каюте капитана тоже одни и те-же…

Всё одно и тоже, вот этот сраный сундук должен стоять именно тут а не там!

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

Скинем этот вопрос на нехватку мозгов разработчиков.

Тут много чего можно было сделать лучше, например начнём с примитива — рандомное расположение предметов в каюте капитана, это делается элементарно.

Подземелья

Это вообще позор. Всё одно и тоже.

Например в диабло 2 подземелья генерируются случайно. В них реально можно заблудится без карты. Сундуки и тп — всё рандомно.

Удача

В игре написано что фортуна нужна везде.

Тогда почему удача не влияет на нашу игру в картишки и кости?

Не по пиратски как-то.

В целом о корсарах 3

Игра ужасна. Её мир такой-же как в первой части, а абордаж хуже.

Советы:

  • Если делаете рпг, то делайте её честной для всех. Старайтесь избегать заранее построенных локаций.Генерируйте их случайно.
  • Никогда не расставляйте предметы заранее, чтоб во всех домах было одно и то-же.
  • Если в игре есть карта, то она должна быть ценна только для текущего игрока, а не для всех кто читает интернет. Исключение — сетевая игра на сервере с миром для всех.
  • Делайте в игре как можно меньше случайных факторов.
  • Если в вашей игре их много — дайте возможность игроку сохранится всегда и везде.
  • Если в вашей игре нет сохранения (типа диабло 2), то смерть героя не должна означать конец игры, а тем более это обидно когда смерть произошла из-за случайного фактора.

Рекомендую:

25.11.2011 | последняя редакция: 26.11.2011 |
  1. Илья
    8.1.2012 в 12:08

    Вы учеников в AS3 как Сергея еще берете? 🙂 Хотелось бы мне(школьнику) поучится у вас.
    Могу писать и на Flash Develop 4, и на Adobe Flash CS5. Но на FD4 есть преимущество, там могу компилить приложения 11 версии с поддержкой Molehill.

    Учусь в 9 классе(в этой году ГИА еще будет), участвовал в школьной и городской олимпиаде по информатике. В школе занял 1-ое место, по городу 7-ое.

    Хотел бы поучится у вас с 0 до профи, чтобы мне легче было изучать 3D кодинг.

  2. admin
    8.1.2012 в 15:52

    Дак, просто перечитай все задания которые я давал Сергею в разделе as3.

  3. Илья
    8.1.2012 в 16:16

    Блог еще не умер, это хорошо. По поводу 1-ого задания — я бы максимально попытался бы использовать возможности AS3, и для простых прямоугольников использовал бы hitTestObject.

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

    Щас для наглядности демки сделаю.

    P.s. и откуда у меня эта автарка берется?

  4. admin
    8.1.2012 в 18:17

    Блогер не умер, просто у него неполадки с интернетом.

    Ну вот собственно используя хиттест, ты получаешь невероятные тормоза — если почитаешь нашу переписку там вообще нету хиттеста.

    Аватарку ты выставил сам. Это называется gravatar.

  5. Илья
    8.1.2012 в 18:39

    Хм… пока попробую с hitTest, т.к. логику проверки еще не придумал.

    P.s. интересно почему ругается на

    getChildByName("box_"+i).gotoAndStop(2);

    Box’ы у меня все таки MovieClip’ы.

  6. Илья
    8.1.2012 в 18:58

    Ай ладно, обошел это ограничение — передал ограничение во временную переменную и сделал ей тип MovieClip.

    Задачу сделал: http://ifolder.ru/27998749 (~10 кб)

  7. Илья
    8.1.2012 в 18:59

    *передал указатель на Box

  8. Илья
    8.1.2012 в 19:15

    Вот на всякий случай код:

    const n_Box:uint = 20;
    
    var txt:TextField = new TextField();
    txt.width = 200;
    addChild(txt);
    
    for(var i:uint=0; i<n_Box; i++){
    	var mc:box=new box();
    	addChild(mc);
    	mc.name = "box_"+i.toString();
    	mc.x = stage.stageWidth*Math.random();
    	mc.y = stage.stageHeight*Math.random();
    	mc.width *= (1+Math.random())^2;
    	mc.height *= 1.3-Math.random();
    	if((Math.random())*10>5){
    		mc.rotation+=90;
    	}
    	mc.addEventListener(MouseEvent.MOUSE_DOWN, onClMcD);
    	function onClMcD(e:MouseEvent):void{
    		e.target.startDrag();
    	}
    	mc.addEventListener(MouseEvent.MOUSE_UP, onClMcU);
    	function onClMcU(e:MouseEvent):void{
    		e.target.stopDrag();
    	}
    	mc.addEventListener(MouseEvent.CLICK, onClMc);
    	function onClMc(e:MouseEvent):void{
    		txt.text = "Name MovieClip: "+e.target.name+"\n"+
    					"X: "+e.target.x+"  Y: "+e.target.y+"\n"+
    					"Width: "+e.target.width+"  Height: "+e.target.height;
    	}
    }
    
    stage.addEventListener(KeyboardEvent.KEY_DOWN, onKD);
    function onKD(e:KeyboardEvent):void{
    	for(var i:uint = 0; i<n_Box; i++){
    		switch(Math.round(Math.random()*3)){
    			case 0: getChildByName("box_"+i).x+=5;
    			break;
    			case 1: getChildByName("box_"+i).x-=5;
    			break;
    			case 2: getChildByName("box_"+i).y+=5;
    			break;
    			case 3: getChildByName("box_"+i).y-=5;
    			break;
    		}
    		if(getChildByName("box_"+i).x<0){
    			getChildByName("box_"+i).x+=stage.stageWidth;
    		}
    		else{
    			if(getChildByName("box_"+i).x>stage.stageWidth){
    				getChildByName("box_"+i).x-=stage.stageWidth;
    			}
    		}
    		if(getChildByName("box_"+i).y<0){
    			getChildByName("box_"+i).y+=stage.stageHeight;
    		}
    		else{
    			if(getChildByName("box_"+i).y>stage.stageHeight){
    				getChildByName("box_"+i).y-=stage.stageHeight;
    			}
    		}
    	}
    }
    
    var ready_st:Boolean;
    var v_mc:MovieClip;
    addEventListener(Event.ENTER_FRAME, onEntFrm);
    function onEntFrm(e:Event):void{
    	for(var i:uint=0; i<n_Box; i++){
    		ready_st = false;
    		for(var ui:uint=1; ui<n_Box; ui++){
    			if((getChildByName("box_"+i).hitTestObject(getChildByName("box_"+ui))) && getChildByName("box_"+i).name!=getChildByName("box_"+ui).name){
    				ready_st = true;
    			}
    		}
    		v_mc = getChildByName("box_"+i) as MovieClip;
    		if(ready_st){
    			v_mc.gotoAndStop(2);
    		}
    		else{
    			v_mc.gotoAndStop(1);
    		}
    	}
    }
  9. admin
    8.1.2012 в 19:28

    В мусорку. На хиттест много ума не надо, делай нормально.

  10. Илья
    8.1.2012 в 21:41

    Yyyeeesss, сделал все-таки.

    Думаю кода достаточно будет, объекты не менял.

    const n_Box:uint = 20;
    
    var txt:TextField = new TextField();
    txt.width = 200;
    addChild(txt);
    
    for(var i:uint=0; i<n_Box; i++){
    	var mc:box=new box();
    	addChild(mc);
    	mc.name = "box_"+i.toString();
    	mc.x = stage.stageWidth*Math.random();
    	mc.y = stage.stageHeight*Math.random();
    	mc.width *= (1+Math.random())^2;
    	mc.height *= 1.3-Math.random();
    	if((Math.random())*10>5){
    		mc.rotation+=90;
    	}
    	mc.addEventListener(MouseEvent.MOUSE_DOWN, onClMcD);
    	function onClMcD(e:MouseEvent):void{
    		e.target.startDrag();
    	}
    	mc.addEventListener(MouseEvent.MOUSE_UP, onClMcU);
    	function onClMcU(e:MouseEvent):void{
    		e.target.stopDrag();
    	}
    	mc.addEventListener(MouseEvent.CLICK, onClMc);
    	function onClMc(e:MouseEvent):void{
    		txt.text = "Name MovieClip: "+e.target.name+"\n"+
    					"X: "+e.target.x+"  Y: "+e.target.y+"\n"+
    					"Width: "+e.target.width+"  Height: "+e.target.height;
    	}
    }
    
    stage.addEventListener(KeyboardEvent.KEY_DOWN, onKD);
    function onKD(e:KeyboardEvent):void{
    	for(var i:uint = 0; i<n_Box; i++){
    		switch(Math.round(Math.random()*3)){
    			case 0: getChildByName("box_"+i).x+=5;
    			break;
    			case 1: getChildByName("box_"+i).x-=5;
    			break;
    			case 2: getChildByName("box_"+i).y+=5;
    			break;
    			case 3: getChildByName("box_"+i).y-=5;
    			break;
    		}
    		if(getChildByName("box_"+i).x<0){
    			getChildByName("box_"+i).x+=stage.stageWidth;
    		}
    		else{
    			if(getChildByName("box_"+i).x>stage.stageWidth){
    				getChildByName("box_"+i).x-=stage.stageWidth;
    			}
    		}
    		if(getChildByName("box_"+i).y<0){
    			getChildByName("box_"+i).y+=stage.stageHeight;
    		}
    		else{
    			if(getChildByName("box_"+i).y>stage.stageHeight){
    				getChildByName("box_"+i).y-=stage.stageHeight;
    			}
    		}
    	}
    }
    
    function hitMyTest(e1, e2:Object):Boolean{
    	var Bx, By:Boolean;
    	if(((e1.y+e1.height/2)>=(e2.y-e2.height/2)) && (e1.y<e2.y)){
    		By = true;
    	}
    	else{
    		if(((e1.y-e1.height/2)<=(e2.y+e2.height/2)) && (e1.y>e2.y)){
    			By = true;
    		}
    	}
    	if(((e1.x+e1.width/2)>=(e2.x-e2.width/2)) && (e1.x<e2.x)){
    		Bx = true;
    	}
    	else{
    		if(((e1.x-e1.width/2)<=(e2.x+e2.width/2)) && (e1.x>e2.x)){
    			Bx = true;
    		}
    	}
    	if(Bx && By){
    		return true;
    	}
    	else{
    		return false;
    	}
    }
    
    var ready_st:Boolean;
    var v_mc:MovieClip;
    addEventListener(Event.ENTER_FRAME, onEntFrm);
    function onEntFrm(e:Event):void{
    	for(var i:uint=0; i<n_Box; i++){
    		ready_st = false;
    		for(var ui:uint=1; ui<n_Box; ui++){
    			if((hitMyTest(getChildByName("box_"+i), getChildByName("box_"+ui))) && getChildByName("box_"+i).name!=getChildByName("box_"+ui).name){
    				ready_st = true;
    			}
    		}
    		v_mc = getChildByName("box_"+i) as MovieClip;
    		if(ready_st){
    			v_mc.gotoAndStop(2);
    		}
    		else{
    			v_mc.gotoAndStop(1);
    		}
    	}
    }
  11. admin
    8.1.2012 в 21:59

    Не понимаю нафига ты трогаешь объекты через getChildByName в энтерфрейме.
    Просто при создании складываешь все боксы в массив.

    Попробуй начать писать простую игру типа головоломки electric box

  12. Илья
    8.1.2012 в 22:27

    Через массив просто не делал не разу :).

  13. Илья
    10.1.2012 в 15:03

    Можно мейл и скайп с 1-ого поста стереть?

    P.s. пока получилась реализация электроцепи, включаются при powerOn, но вот щас времени на разработку мало становится — каникулы заканчиваются. Игру нужно будет показывать когда полностью сделаю или во время разработки?

  14. admin
    10.1.2012 в 19:46

    Когда закончишь — покажешь, только делай не 1 в 1, а придумай что-нибудь своё.
    Заведи свой блог в интернете.
    Мыло и скайп потер.

  15. Илья
    10.1.2012 в 21:06

    Блин, я уже начал в точности копировать, ладно попробую че-нить придумать.

  16. Илья
    10.1.2012 в 21:07

    Ммм… На разработку думаю может около месяца уйти, свободное время поубавилось.

  17. admin
    10.1.2012 в 21:09

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

  18. admin
    11.1.2012 в 10:30

    Он и должен все объекты проверять. Ты можешь не парится по этому поводу у тебя не шутер в котором летят пули.

  19. Илья
    11.1.2012 в 12:14

    Что у меня и сложное пока, так это придумать свое объекты для взаимодействия. Пока планирую, что при поступление энергии в один источник, она будет там до конца, то есть отключения не будет происходить — для экономии просчетов. А то понаберу объектов и будет он каждый кадр все объекты проверять.

    P.s. вот скрин http://i29.fastpic.ru/big/2012/0111/08/6ceed132b1cf9bb018c34ab02b72e208.png