Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Разделение программы на классы (http://www.flasher.ru/forum/showthread.php?t=191788)

zzSpirit 07.01.2013 11:48

Разделение программы на классы
 
Привет всем,
Изучаю actionScript недолго и как мне кажется, он себя очень странно ведет. Что ни начну делать, всё ему не так. Хотел узнать почему.

Всё работает, если пишу только в главном классе. А я хочу разделить прогу на 2 класса - Main и Beatle(это у меня игрок).

1). К примеру, хочу сделать обычное перемещение с помощью стрелок. На мой взгляд логично, если всё это будет в классе Beatle. Задается скорость передвижения, добавляются слушатели событий и сами функции передвижения, а в Main просто создается объект класса Beatle.
В конструкторе класса Beatle добавляю слушатель клавиатуры:
Код AS3:

addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownEvent);

Так не работает, пробую еще так и так:
Цитата:

stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownEvent);
Цитата:

this.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownEvent);
Цитата:

Beatle.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownEvent);
Никак не работает. Значит, слушатель событий клавиатуры из класса Beatle назначить неполучается. Ну, фиг с ним, решил в классе Main написать слушатели событий клавиатуры и вызывать метод класса Beatle для передвижения, типа Beatle.walkLeft();
Добавляю слушатель событий в класс Main, работает:
Код AS3:

stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownEvent);

Добавляю функцию onKeyDownEvent() в класс Main, в которой вызываю метод beatle.walkLeft(); Да, при нажатии любой клавиши на клавиатуре объект должен двигаться влево:
Код AS3:

public function onKeyDownEvent():void
{
        beatle.walkLeft();
}

Но метод объекта beatle не вызывается из этой функции, хотя из конструктора класса Main он вызывается.

Даже на таких простых вещах у меня уже проблемы, AS3 просто не дает мне разделить код на классы такими способами, хотя на мой взгляд они вполне логичны и удобны:
1). Слушатели событий и функции слушателей в классе Beatle, а в классе Main только создается объект класса Beatle и добавляется на сцену.
2). В классе Main слушатели событий и функции слушателей, которые вызывают нужные методы объектов, типа beatle.walkLeft();

Как разделить на классы программу так, что бы это работало?

AleXXX 07.01.2013 12:05

Код в студию, думаю шаманов гораздо меньше чем тех, кто просто мог бы помочь. ;)

Wolsh 07.01.2013 12:11

Вам бы включить строгий режим компиляции, чтобы получать от компилятора сообщения об ошибках.
Строго говоря, public function onKeyDownEvent():void вообще не должно компилиться, так как обработчик события обязан принимать объект события в качестве аргумента. То есть
Код AS3:

public function onKeyDownEvent(event:KeyboardEvent):void

Цитата:

Но метод объекта beatle не вызывается из этой функции, хотя из конструктора класса Main он вызывается.
Очевидно потому, что в конструкторе Вы создаете переменную beatle, в которую и сохраняете ссылку на создаваемый экземпляр Beatle. И внутри конструктора эта переменная видна, но как только конструктор отработал (как и для любой функции), все локальные переменные функции больше не существуют. То есть Ваш обработчик понятия не имеет, что такое beatle. Надо сделать beatle членом класса, чтобы он стал видимым для всех остальных членов (методов). Объявите его в теле класса, а не конструктора.

Добавлено через 16 минут
Что же касается попыток подписаться на события клавиатуры из класса Beatle, то надо понимать, что это событие не многоцелевое, как ентерфрейм например. Его получает только стейдж и тот дисплейный объект, который в момент события находился в фокусе. То есть экземпляр Вашего битла сможет получать события клавиатуры только после того, как будет добавлен на сцену и получит фокус. Поэтому единственным "перспективным" решением было подписать stage. Однако Вы сделали это видимо опять же в конструкторе класса Beatle. А в конструкторе у объекта нет ссылки на стейдж, так как он только создается и еще не добавлен на сцену.
Обо всем этом Вам должен был рассказать компилятор, лучший друг программиста.

zzSpirit 07.01.2013 16:34

Wolsh, большое спасибо! Теперь многое прояснилось.
Включил строгий режим, помогло: компилятор нашел ошибки, о которых я даже не подозревал.
Теперь ясно, почему методы класса Beatle не работали. Действительно очевидно.

Цитата:

Сообщение от Wolsh (Сообщение 1114041)
public function onKeyDownEvent():void вообще не должно компилиться

Упс, это я уже в сообщении ошибку допустил.

Код AS3:

Поэтому единственным "перспективным" решением было подписать stage.

Разобрался и понял, почему "перспективным" было написано в кавычках. :)

Не разобрался только в том, как поделить программу на классы более эстетично.
Ведь несколько слушателей событий клавиатуры в классе Main для разных методов разных объектов - это не правильно, да?
Можно сделать класс Keys, который будет заниматься событиями клавиатуры, но он не сможет вызывать методы других экземпляров, к примеру beatle.moveUp без ссылки на них, а писать код перемещения персонажа в классе Keys неправильно, поскольку это код класса Beatle.

Как правильно поделить программу на классы?

samana 07.01.2013 17:05

Если вы хотите сделать управление внутри класса Beatle, то это может выглядеть примерно так (простой вариант):
Код AS3:

package  
{
        import flash.display.Sprite;
        import flash.events.Event;
        import flash.events.KeyboardEvent;
 
        /**
        * ...
        * @author Samana
        */

        public class Beatle extends Sprite
        {
                private var keyLeft:Boolean=false;
                private var keyRight:Boolean=false;
 
                public function Beatle()
                {
                        addEventListener(Event.ADDED_TO_STAGE, addedToStage);
                }
 
                private function addedToStage(e:Event):void
                {
                        removeEventListener(Event.ADDED_TO_STAGE, addedToStage);
 
                        addEventListener(Event.REMOVED_FROM_STAGE, removedFromStage);
                        addEventListener(Event.ENTER_FRAME, enterFrame);
                        stage.addEventListener(KeyboardEvent.KEY_DOWN, stage_keyDown);
                        stage.addEventListener(KeyboardEvent.KEY_UP, stage_keyUp);
                }
 
                private function enterFrame(e:Event):void
                {
                        if (keyLeft) x -= 5;
                        if (keyRight) x +=5;
                }
 
                private function stage_keyUp(e:KeyboardEvent):void
                {
                        if (e.keyCode == 37) keyLeft = false;
                        if (e.keyCode == 39) keyRight = false;
                }
 
                private function stage_keyDown(e:KeyboardEvent):void
                {
                        if (e.keyCode == 37) keyLeft = true;
                        if (e.keyCode == 39) keyRight = true;
                }
 
                private function removedFromStage(e:Event):void
                {
                        removeEventListener(Event.REMOVED_FROM_STAGE, removedFromStage);
                        removeEventListener(Event.REMOVED_FROM_STAGE, removedFromStage);
                        removeEventListener(Event.ENTER_FRAME, enterFrame);
                        stage.removeEventListener(KeyboardEvent.KEY_DOWN, stage_keyDown);
                        stage.removeEventListener(KeyboardEvent.KEY_UP, stage_keyUp);
                }
 
        }
 
}

Тогда в Маин классе остаётся только добавить экземпляр класса Beatle на сцену.

Wolsh 07.01.2013 18:49

Цитата:

Как правильно поделить программу на классы?
Этот вопрос относится к Проектированию, довольно самостоятельной дисциплине. Здесь нет однозначных ответов, все зависит от проекта целиком. Одно дело, пока Вы пытаетесь реализовать движение битла в вакууме, и совсем другое, когда он двигается по карте с препятствиями и NPC. Здесь наверное стоит почитать общие вещи по проектированию, особенно ООП (так как AS3 ориентирован именно на ООП).
Цитата:

а писать код перемещения персонажа в классе Keys неправильно, поскольку это код класса Beatle.
Категорически несогласен. Каждый класс должен отвечать только за то, что происходит у него "внутри", с его частями, его подчиненными. Как раз здесь очень яркий пример, почему. Чтобы обеспечить логику движения по карте вашему экземпляру битла, Вам придется подарить ему знание всей карты и описать логику взаимодействия со всеми предметами и неписями, ловушками и бонусами всех возможных классов. То есть Ваш битл должен будет содержать в себе буквально копию всей остальной программы. А ведь он — не более чем еще один объект на карте. Подчиненный Карты. Карта знает "себя" и знает битла. Ей и решать, куда и как он должен/может двигаться, с кем он встретился и дошел ли до конца уровня.
Всех благ.

expl 07.01.2013 19:10

Наверно, на данном этапе Вашего развития лучше не задавать вопросы про проектирование и ООП - вас здесь запутают только, и слово "правильно" хорошо бы вычеркнуть из лексекона. Просто посмотрите как делают люди:
http://xitri.com/
http://www.flasher.ru/forum/showthread.php?t=83431

Если позже, когда напишите что-то минимально рабочее Вас заинтересует архитектура, то подхода к играм здесь 3:
- MVC (хорош, где много GUI и всяких расчетов что за сколько при каких скидках и в какой промежуток времени можно купить и использовать, для социалок, короче)
- Entity system (хорош для реал-тайм аркад) (фреймворк Starling, говорят, исползьует - сам не пробовал, Unity3d тоже, да что там, старый Контрстрайк на нём работает)
- "ненавижу архитектурных астронавтов" - подход, _реально_ используемый при написании 30% социалок и 80% аркад на флеше. Использование этого подхода объясняется высоким уровнем IQ среди программистов, достаточным, чтобы не запутаться в макаронах, коие они генерируют. Ну ещё простотой небольшого числа аркад, где Entity-system мало чего даёт.

zzSpirit 08.01.2013 08:53

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

expl, уже видел эту серию уроков о создании аркады, для меня пока сложновато, хотя немного я все же разобрал эти примеры.
Первая ссылка очень понравилась, очень интересный блог, просто супер! Полезная информация, буду изучать сейчас. Спасибо.

Wolsh, не совсем понимаю. Если писать код перемещения персонажа в классе Keys, то мне ведь придется писать свойства класса Beatle в классе Keys, такие как speed, например. И много чего другого из класса Beatle... А свойство speed должно быть в классе Beatle, как его изменять из другого класса, кроме класса main - тоже загадка для меня пока что.

2ALL В общем решил так - понимания АС3 у меня пока что мало, вопросы задавать нет смысла, т.к. их слишком много, лучше поразбираю исходники всякие, посмотрю что и как сделано, что бы не изобретать велосипеды. Большинство вопросов после этого сами собой отпадут.

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

Ищу этот сайт, не могу найти. Хочу разобрать все эти примеры и уже смогу что-то делать на АС3.
Быть может если кто знает этот сайт, киньте ссылку, помню там очень хорошие примеры были.

Добавлено через 1 час 35 минут
Еще хотел вот что спросить:
Большинство примеров, которые я видел написаны на AS2, в т.ч. и по ссылке, которую мне дал тов. Экспл.
Я сразу начал изучать AS3 и не стал вникать в AS2 и AS1, чтобы не запутаться еще сильнее.
Правильный ли это подход? Дело в том, что хочется уже побыстрее изучить AS3 и сделать небольшую игрушку.

Добавлено через 1 час 37 минут
P.S. А вообще, до этого на PHP кодил, поэтому AS3 мне кажется очень запутанным и мудреным, хотя чем больше начинаю его понимать, тем более логичным мне он кажется.

Добавлено через 2 часа 0 минут
Нашел сайт, про который я писал. Вот он - http://www.freeactionscript.com/
Может кому пригодится, очень хорошие исходники.

Wolsh 08.01.2013 11:06

Цитата:

Если писать код перемещения персонажа в классе Keys
Во-первых, я такого не говорил. Я не согласен с тем, что код перемещения должен писаться в том, кто перемещается. Во-вторых, это Вам кажется, что скорость обязана храниться в персонаже. Прежде всего потому, что Вы еще не готовы мысленно разделять данные и отображение, и не представляете другого способа, кроме как хранить всю инфу о персонаже в его "картинке". Попробуйте рассматривать все изображения на карте как просто изображения. Управляемые логикой на уровне карты. Карта в любом случае знает все объекты, которые на ней находятся — они ее дети. Намного проще проверять коллизии и управлять перемещением на этом уровне, а не изнутри отдельных объектов, абсолютно ничего не знающих об окружающем мире.
Но, возможно, для этого разговора еще просто не настало время.
Класс Keys должен просто представлять состояние клавиатуры. Никакой другой ответственности не надо на него вешать.
Логическое ядро по таймеру или ентерфрейм пытается построить текущее состояние карты для нового "кадра". Опрашивает клавиатуру, какие клавиши нажаты, анализирует положение объектов: может ли двигаться перс в заданном направлении, не столкнулся ли он со стеной, или бонусом, или врагом, не достиг ли финиша и жив ли еще вообще)). Направляет врагов, в поле зрения которых попал перс, в его сторону, и т.д. Еще раз повторюсь: персонаж в вакууме это одно. Вы можете тупо прибавлять/отнимать скорость от его координаты по нажатию клавиши. Персонаж как часть системы — абсолютно другое. Здесь его движение зависит от состояния всей системы, а вовсе не только от нажатой клавиши. Просто представьте аналогию: вы с другом играете в шашки. Шашкам не надо знать, как ходить. Они просто изображают себя на карте. А перемещение делаете Вы. И Вы знаете, куда и как надо ходить, кто будет "срублен" и закончилась ли игра. Не шашки.

zzSpirit 08.01.2013 12:52

Цитата:

Сообщение от Wolsh (Сообщение 1114239)
Во-первых, я такого не говорил. Я не согласен с тем, что код перемещения должен писаться в том, кто перемещается. Во-вторых, это Вам кажется, что скорость обязана храниться в персонаже. Прежде всего потому, что Вы еще не готовы мысленно разделять данные и отображение, и не представляете другого способа, кроме как хранить всю инфу о персонаже в его "картинке".

Но класс Beatle же создан для того, чтобы там хранить его свойства(например speed) + методы(например move_up). Если нет, то зачем он нужен?

После прочтения Вашего сообщения, мне теперь кажется наиболее правильной такая структура:

Класс Main - это главный контроллер, в классе Beatle должны быть свойства + методы, но изменять значения свойств и вызывать методы должен класс Main.
Пример:
Main в ентерфрейме обнаруживает, что Keys вернул клавишу DOWN, запрашивает есть ли объект внизу с помощью какого-то другого класса, если объекта внизу никакого нет, то вызывает beatle.move_down, если объект есть - не вызывает.

P.S. Что посоветуете изучить/прочитать, чтобы усвоить проектирование ООП приложений?

Wolsh 08.01.2013 14:29

Цитата:

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

Если нет, то зачем он нужен?
Для отображения персонажа, вестимо. Для управления его анимацией, поворотами, ходьбой, прыжками и прочими визуальными эффектами. А что он, по-вашему, должен хранить? Если бы персонажей было сто, был бы смысл каждому из них хранить свою "скорость". А когда он один, достаточно карте иметь одно приватное свойство "скорость персонажа". И то только если эта скорость на самом деле переменная.
Цитата:

в классе Beatle должны быть свойства + методы
Безусловно. Свойства и методы, относящиеся к данному экземпляру. Методы "заплакать", "подпрыгнуть", "присесть" и т.п., то есть "перейти на такой-то кадр анимации". И свойства типа ширина и высота. Потому что даже х и у нельзя считать его собственными свойствами "в вакууме". Это его свойства в системе координат его контейнера (карты). Отсюда и методы типа moveDown выглядят как-то смешно. С таким же успехом можно написать beatle.y += persSpeed;. Вот только логики здесь будет гораздо больше: система изменяет свои части, а не части изменяют систему. Это называется словом Иерархия. А его противоположность — Хаос, когда шашки сами решают куда им ходить.
Цитата:

Что посоветуете изучить/прочитать, чтобы усвоить проектирование ООП приложений?
Ну хотя бы ActionScript 3.0. Шаблоны проектирования.

zzSpirit 08.01.2013 16:03

Ура! Я понял о чем Вы говорили и написал передвижение своего жука по всем правилам. Прочитал Ваш последний пост и всё сходится. Теперь намного лучше понимаю ООП.

gagaga 08.01.2013 16:07

Цитата:

Сообщение от samana (Сообщение 1114067)
Если вы хотите сделать управление внутри класса Beatle, то это может выглядеть примерно так (простой вариант):
Код AS3:

                private function stage_keyUp(e:KeyboardEvent):void 
                {
                        if (e.keyCode == 37) keyLeft = false;
                        if (e.keyCode == 39) keyRight = false;
                }
 
                private function stage_keyDown(e:KeyboardEvent):void
                {
                        if (e.keyCode == 37) keyLeft = true;
                        if (e.keyCode == 39) keyRight = true;
                }

Тогда в Маин классе остаётся только добавить экземпляр класса Beatle на сцену.

это не правильно... 1 кейкод не может быть одновременно равен 2-м значениям, нужно использовать конструкцию switch или хотябы if else if

zzSpirit 08.01.2013 16:41

Цитата:

Сообщение от gagaga (Сообщение 1114276)
это не правильно... 1 кейкод не может быть одновременно равен 2-м значениям, нужно использовать конструкцию switch или хотябы if else if

Хм... В моем примере также сделано. Но где тут кейкод равняется двум значениям? Такого нету.
А если if else if или switch использовать - то нажатия двух клавиш одновременно не будут фиксироваться.
По-моему пример юзера samana правильный.

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

TWETTI 08.01.2013 16:49

В вашем классе Keys функция onKeyDown и onKeyUp должна выглядеть так:
Код AS3:

private function onKeyDown(event:KeyboardEvent):void
{
        if (event.keyCode == 38)
        {
                up = true;
        }
        else if (event.keyCode == 40)
        {
                down = true;
        }
        else if (event.keyCode == 37)
        {
                left = true;
        }
        else if (event.keyCode == 39)
        {
                right = true;
        }
}

Одновременно клавиши нажиматься не могут. Нажали вниз, произошло события KeyboardEvent.KEY_DOWN, down стал равен true, затем через несколько миллисекунд нажали вправо, произошло тоже самое событие, теперь down и right равны true, остальные переменные false. При отпускании этих кнопок произойдут два события KeyboardEvent.KEY_UP и по очереди down и up будут вновь равны false.

Добавлено через 3 минуты
Цитата:

Сообщение от zzSpirit (Сообщение 1114285)
Только не совсем понимаю, если нажаты 2 и более клавиш, то через кадр по очереди будут разные коды в e.keyCode?

Да.
Цитата:

Сообщение от zzSpirit (Сообщение 1114285)
Как тогда определяется нажатие двух клавиш, ведь действительно e.keyCode не может равняться двум значениям, если он не массив...

Код AS3:

if (down && up)
{
    trace("Нажата клавиша вниз и вверх");
}


zzSpirit 08.01.2013 16:57

Цитата:

Сообщение от TWETTI (Сообщение 1114289)
Одновременно клавиши нажиматься не могут. Нажали вниз, произошло события KeyboardEvent.KEY_DOWN, down стал равен true, затем через несколько миллисекунд нажали вправо, произошло тоже самое событие, теперь down и right равны true, остальные переменные false. При отпускании этих кнопок произойдут два события KeyboardEvent.KEY_UP и по очереди down и up будут вновь равны false.

Только что проверил, запустил и нажал сразу две клавиши. Жук двигался по диагонали, output такой:
Код AS3:

keyCode: 40
keyCode: 39
keyCode: 39
keyCode: 39
keyCode: 39
keyCode: 39
и так далее... Дальше всегда 39

Получается, клавиша вниз всегда оставалась true, даже тогда, когда аутпут показывал только клавишу влево. Ну, все правильно, хотя и не очень понятно, как в следующих кадрах АС3 знал, что все еще нажата клавиша вниз, если аутпут всегда выдавал только 39 на каждом кадре. Загадка.

Добавлено через 5 минут
Цитата:

Сообщение от TWETTI (Сообщение 1114289)
В вашем классе Keys функция onKeyDown и onKeyUp должна выглядеть так:

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

Добавлено через 7 минут
Цитата:

Сообщение от TWETTI (Сообщение 1114289)
Код AS3:

if (down && up)
{
    trace("Нажата клавиша вниз и вверх");
}


Нет, я имел ввиду не как определить что нажаты две клавиши, а как AS3 определяет, что нажаты 2 клавиши. Посмотрите output. Там кейкод клавиши которая первая была нажата, а потом уже идет только кейкод клавиши, которая была нажата второй. Они идут не по очереди.

Добавлено через 14 минут
Изменил ИФы на Свитчи. И код лучше читается и меньше по размеру и правильней. Теперь там такие конструкции:
Код AS3:

                private function onKeyDown(event:KeyboardEvent):void
                {
                        switch(event.keyCode)
                        {
                                case 38 : up = true; break;
                                case 40 : down = true; break;
                                case 37 : left = true; break;
                                case 39 : right = true; break;
                        }
                }


TWETTI 08.01.2013 17:13

Цитата:

Сообщение от zzSpirit (Сообщение 1114296)
Нет, я имел ввиду не как определить что нажаты две клавиши, а как AS3 определяет, что нажаты 2 клавиши. Посмотрите output. Там кейкод клавиши которая первая была нажата, а потом уже идет только кейкод клавиши, которая была нажата второй. Они идут не по очереди.

AS3 никак не определяет что две клавиши нажаты. Это должны делать вы сами.

zzSpirit 08.01.2013 17:13

Обновленный архив с проектом загрузил по той же ссылке.

Добавлено через 1 минуту
Цитата:

Сообщение от TWETTI (Сообщение 1114302)
AS3 никак не определяет что две клавиши нажаты. Это должны делать вы сами.

Но ведь если 2 клавиши нажаты, он в output пишет сначала кейкод первой клавиши, а потом постоянно кей-код другой клавиши. (мой output написал выше)
При этом обе клавиши равны true. Хотя кейкод первой клавиши он больше не получал.

Добавлено через 4 минуты
Ой, опять туплю. Всё правильно. Первая клавиша не должна стать false, если не поступило команды о ее отпускании. Извиняюсь.

TWETTI 08.01.2013 17:18

Цитата:

Сообщение от zzSpirit (Сообщение 1114303)
Но ведь если 2 клавиши нажаты, он в output пишет сначала один кейкод, а потом постоянно кей-код другой. (мой output написал выше)
При этом обе клавиши равны true.

Все правильно. В output выводится информация о том, какая клавиша сейчас нажата. Обе клавишы равны true потому, что вы в коде это учли.

zzSpirit 08.01.2013 17:31

Да. Уже написал выше. Всё правильно. Не было команды false для первой клавиши, поэтому она всегда была true, а в output выводился кейкод второй клавиши.

Добавлено через 1 час 24 минуты
Wolsh, Благодарю за книгу. Буду ее очень внимательно изучать. Благодаря Вашим подробным обьяснениям, многое понял. Большое Вам спасибо!


Часовой пояс GMT +4, время: 11:55.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.