volod
27.01.2010, 14:37
Доброго всем.
Потратил сегодня три часа на разбор кода, правку и тестирование. И решил поделиться накопанным. Начинающим разработчикам возможно облегчит жизнь.
Чего мы делаем (Цель задачи) - сделать возможность отображать в игре и нормально работать с большим количеством институтов, чем предусмотрено оригинальной игрой.
Задача, возможно, весьма специфическая, но на её примере можно рассмотреть общий принцип редактирования интерфейсов на питоне.
Для справки: Есть более простое решение, использованное в Fall from Heaven (изменены размеры и шрифты панелей, за счёт чего освобождено дополнительное место для новых категорий институтов).
В моём случае мне нужно было впихнуть в экран 10 категорий институтов, чего никак не получалось добиться простым изменением размеров (только при 6-ом шрифте и совсем узеньких панелях, но тогда экран становится совсем нечитабельным). Я решил разделить категории на несколько экранов и сделать возможность перехода между ними.
Итак, поехали.
Копируем файлик CvCivicsScreen.py из каталога Assets/Python/Screens оригинальной игры в такой же каталог внутри своего мода. Файлы этого каталога задают прорисовку различных игровых экранов. Конкретно в этом файле реализуется экран смены институтов власти.
Открываем файлик (можно какой-нибудь спецефической средой разработки, я пользовался обычным блокнотом) и находим там строчки с описанием класса:
class CvCivicsScreen:
....
Здесь задаются всевозможные параметры будущего окна и внутренние переменные, необходимые для его прорисовки. Пока просто поправим размеры панелей так, что бы они соответствовали нашим запросам (мне пришлось несколько раз загрузить игру, чтобы подобрать нужные значения).
Например, я планировал изображать одновременно пять категорий. И выставил следующие значения:
self.HEADINGS_WIDTH = 190 (ширина панели одного института)
self.HEADINGS_SPACING = 7 (расстояние между панелями)
self.TEXT_MARGIN = 12 (размер шрифта)
Остальные параметры я оставил без изменения.
Далее добавим два определителя (будущие кнопки для перехода)
self.NEXT_NAME = "CivicsNext"
self.PREV_NAME = "CivicsPrev"
и их координаты:
self.X_NEXT = 300
self.Y_NEXT = 726
self.X_PREV = 150
self.Y_PREV = 726
Так же нам понадобятся две внутренние переменные (для чего, покажу далее):
self.SCREEN_NUMBER = 0 (номер экрана, отображаемого в данный момент)
self.CHANGE_SCREEN_NUMBER = 0 (флаг, отмечающий, что было переключение экрана)
И свяжем наши кнопки с функциями, которые напишем позже (добавим два определителя в блок):
self.CivicsScreenInputMap = {
self.BUTTON_NAME : self.CivicsButton,
self.TEXT_NAME : self.CivicsButton,
self.EXIT_NAME : self.Revolution,
self.NEXT_NAME : self.Next,
self.PREV_NAME : self.Previous,
self.CANCEL_NAME : self.Cancel,
}
С определителями всё, переходим непосредственно к коду.
Сначала сделаем так, чтобы на экране рисовалось только нужное нам количество категорий.
Находим функцию
def drawAllButtons(self):
В её начало пихаем строчки:
cnt = 5*(self.SCREEN_NUMBER+1)
if (cnt > gc.getNumCivicOptionInfos()): cnt = gc.getNumCivicOptionInfos()
Эти строки создают переменную cnt, в которой лежит индекс категории институтов, которым мы закончим прорисовку.
Меняем строку:
for i in range(gc.getNumCivicOptionInfos()):
на
for i in range(5*self.SCREEN_NUMBER,cnt):
То есть, мы запустили цикл по ограниченному установленными нами индексами количеству категорий.
Теперь изменим координаты прорисовки панели с очередной категорией. Фактически нам нужно менять только X-координату.
Вместо
fX = self.HEADINGS_SPACING + (self.HEADINGS_WIDTH + self.HEADINGS_SPACING) * i
пишем
fX = self.HEADINGS_SPACING + (self.HEADINGS_WIDTH + self.HEADINGS_SPACING) * (i - 5 * self.SCREEN_NUMBER)
Вот и всё, теперь панели с категориями будут прорисовываться аккурат по пять штук в ряд в зависимости от номера экрана.
Теперь добавим тоже самое в прорисовку панелей с описаниями текущего института. Делаем те же операции в функции
def drawAllHelpText(self):
Но здесь нужно изменить ещё кое-что. Поскольку панели описания и текст в них рисуются отдельно, нужно в функции
def drawHelpText(self, iCivicOption):
так же изменить строку
fX = self.HEADINGS_SPACING + (self.HEADINGS_WIDTH + self.HEADINGS_SPACING) * iCivicOption
на
fX = self.HEADINGS_SPACING + (self.HEADINGS_WIDTH + self.HEADINGS_SPACING) * (iCivicOption - 5 * self.SCREEN_NUMBER)
С коррекцией прорисовки закончили, теперь перейдём к переключению экранов.
В основной функции
def interfaceScreen (self):
в самом начале функции заменяем блок
if screen.isActive(): return
на
if (screen.isActive() and (self.CHANGE_SCREEN_NUMBER == 0)): return
то есть, выходим из функции при условии, что экран не только уже был открыт, но и не было перехода между экранами. Ведь именно вызовом этой функции мы дальше будем перерисовывать экран при переходе.
добавляем монтирование кнопок перехода на панель (вставляем перед или за блоком, монтирующим кнопку отмены)
if(self.SCREEN_NUMBER * 5 < gc.getNumCivicOptionInfos() - 5):
screen.setText(self.NEXT_NAME, "Background", u"<font=4>" + localText.getText("TXT_KEY_SCREEN_NEXT", ( )).upper() + u"</font>", CvUtil.FONT_RIGHT_JUSTIFY, self.X_NEXT, self.Y_NEXT, self.Z_TEXT, FontTypes.TITLE_FONT, WidgetTypes.WIDGET_GENERAL, 1, 0)
if(self.SCREEN_NUMBER > 0):
screen.setText(self.PREV_NAME, "Background", u"<font=4>" + localText.getText("TXT_KEY_SCREEN_PREV", ()).upper() + u"</font>", CvUtil.FONT_CENTER_JUSTIFY, self.X_PREV, self.Y_PREV, self.Z_TEXT, FontTypes.TITLE_FONT, WidgetTypes.WIDGET_GENERAL, 1, 0)
То есть, рисуем кнопки в заданные им координаты и проверяем условия: для продвижения экрана, что экран не последний, для возврата, что экран не первый.
Не забываем дать описатели ключам TXT_KEY_SCREEN_NEXT (Далее) и TXT_KEY_SCREEN_PREV (Назад) в текстовых xml-ках мода, для внятного отображения названия кнопок.
Строчку
self.setActivePlayer(gc.getGame().getActivePlayer())
теперь нужно выполнять только при условии
if (self.CHANGE_SCREEN_NUMBER == 0):
self.setActivePlayer(gc.getGame().getActivePlayer())
То есть вызывать эту функцию мы будем только, если экран открыт первый раз. В этой функции производится начальная инициализация выбора игрока. И поскольку мы хотим, что бы при переключении экранов у нас сохранялся выбор на предыдущем и можно было одновременно сменить несколько институтов, то нам не нужно вызывать эту функцию.
И, наконец, сбрасываем флаг переключения экранов:
self.CHANGE_SCREEN_NUMBER = 0
Собственно, здесь всё.
Теперь опишем функции переключения экранов (Вперёд/Назад)
def Next(self, inputClass):
screen = self.getScreen()
self.SCREEN_NUMBER = self.SCREEN_NUMBER + 1
self.CHANGE_SCREEN_NUMBER = 1
self.interfaceScreen()
return 0
def Previous(self, inputClass):
screen = self.getScreen()
self.SCREEN_NUMBER = self.SCREEN_NUMBER - 1
self.CHANGE_SCREEN_NUMBER = 1
self.interfaceScreen()
return 0
Первая увеличивает, вторая уменьшает номер экрана. После чего выставляется флаг переключения и вызывается перерисовка экрана.
Всё, сохраняем наш файлик, запускаем, проверяем :)
Теперь можно описать любое количество категорий институтов власти в XML и свободно использовать их в игре.
Аналогично можно переделать под свои нужды и любой экран или другой элемент интерфейса.
С уважением, Волод.
Потратил сегодня три часа на разбор кода, правку и тестирование. И решил поделиться накопанным. Начинающим разработчикам возможно облегчит жизнь.
Чего мы делаем (Цель задачи) - сделать возможность отображать в игре и нормально работать с большим количеством институтов, чем предусмотрено оригинальной игрой.
Задача, возможно, весьма специфическая, но на её примере можно рассмотреть общий принцип редактирования интерфейсов на питоне.
Для справки: Есть более простое решение, использованное в Fall from Heaven (изменены размеры и шрифты панелей, за счёт чего освобождено дополнительное место для новых категорий институтов).
В моём случае мне нужно было впихнуть в экран 10 категорий институтов, чего никак не получалось добиться простым изменением размеров (только при 6-ом шрифте и совсем узеньких панелях, но тогда экран становится совсем нечитабельным). Я решил разделить категории на несколько экранов и сделать возможность перехода между ними.
Итак, поехали.
Копируем файлик CvCivicsScreen.py из каталога Assets/Python/Screens оригинальной игры в такой же каталог внутри своего мода. Файлы этого каталога задают прорисовку различных игровых экранов. Конкретно в этом файле реализуется экран смены институтов власти.
Открываем файлик (можно какой-нибудь спецефической средой разработки, я пользовался обычным блокнотом) и находим там строчки с описанием класса:
class CvCivicsScreen:
....
Здесь задаются всевозможные параметры будущего окна и внутренние переменные, необходимые для его прорисовки. Пока просто поправим размеры панелей так, что бы они соответствовали нашим запросам (мне пришлось несколько раз загрузить игру, чтобы подобрать нужные значения).
Например, я планировал изображать одновременно пять категорий. И выставил следующие значения:
self.HEADINGS_WIDTH = 190 (ширина панели одного института)
self.HEADINGS_SPACING = 7 (расстояние между панелями)
self.TEXT_MARGIN = 12 (размер шрифта)
Остальные параметры я оставил без изменения.
Далее добавим два определителя (будущие кнопки для перехода)
self.NEXT_NAME = "CivicsNext"
self.PREV_NAME = "CivicsPrev"
и их координаты:
self.X_NEXT = 300
self.Y_NEXT = 726
self.X_PREV = 150
self.Y_PREV = 726
Так же нам понадобятся две внутренние переменные (для чего, покажу далее):
self.SCREEN_NUMBER = 0 (номер экрана, отображаемого в данный момент)
self.CHANGE_SCREEN_NUMBER = 0 (флаг, отмечающий, что было переключение экрана)
И свяжем наши кнопки с функциями, которые напишем позже (добавим два определителя в блок):
self.CivicsScreenInputMap = {
self.BUTTON_NAME : self.CivicsButton,
self.TEXT_NAME : self.CivicsButton,
self.EXIT_NAME : self.Revolution,
self.NEXT_NAME : self.Next,
self.PREV_NAME : self.Previous,
self.CANCEL_NAME : self.Cancel,
}
С определителями всё, переходим непосредственно к коду.
Сначала сделаем так, чтобы на экране рисовалось только нужное нам количество категорий.
Находим функцию
def drawAllButtons(self):
В её начало пихаем строчки:
cnt = 5*(self.SCREEN_NUMBER+1)
if (cnt > gc.getNumCivicOptionInfos()): cnt = gc.getNumCivicOptionInfos()
Эти строки создают переменную cnt, в которой лежит индекс категории институтов, которым мы закончим прорисовку.
Меняем строку:
for i in range(gc.getNumCivicOptionInfos()):
на
for i in range(5*self.SCREEN_NUMBER,cnt):
То есть, мы запустили цикл по ограниченному установленными нами индексами количеству категорий.
Теперь изменим координаты прорисовки панели с очередной категорией. Фактически нам нужно менять только X-координату.
Вместо
fX = self.HEADINGS_SPACING + (self.HEADINGS_WIDTH + self.HEADINGS_SPACING) * i
пишем
fX = self.HEADINGS_SPACING + (self.HEADINGS_WIDTH + self.HEADINGS_SPACING) * (i - 5 * self.SCREEN_NUMBER)
Вот и всё, теперь панели с категориями будут прорисовываться аккурат по пять штук в ряд в зависимости от номера экрана.
Теперь добавим тоже самое в прорисовку панелей с описаниями текущего института. Делаем те же операции в функции
def drawAllHelpText(self):
Но здесь нужно изменить ещё кое-что. Поскольку панели описания и текст в них рисуются отдельно, нужно в функции
def drawHelpText(self, iCivicOption):
так же изменить строку
fX = self.HEADINGS_SPACING + (self.HEADINGS_WIDTH + self.HEADINGS_SPACING) * iCivicOption
на
fX = self.HEADINGS_SPACING + (self.HEADINGS_WIDTH + self.HEADINGS_SPACING) * (iCivicOption - 5 * self.SCREEN_NUMBER)
С коррекцией прорисовки закончили, теперь перейдём к переключению экранов.
В основной функции
def interfaceScreen (self):
в самом начале функции заменяем блок
if screen.isActive(): return
на
if (screen.isActive() and (self.CHANGE_SCREEN_NUMBER == 0)): return
то есть, выходим из функции при условии, что экран не только уже был открыт, но и не было перехода между экранами. Ведь именно вызовом этой функции мы дальше будем перерисовывать экран при переходе.
добавляем монтирование кнопок перехода на панель (вставляем перед или за блоком, монтирующим кнопку отмены)
if(self.SCREEN_NUMBER * 5 < gc.getNumCivicOptionInfos() - 5):
screen.setText(self.NEXT_NAME, "Background", u"<font=4>" + localText.getText("TXT_KEY_SCREEN_NEXT", ( )).upper() + u"</font>", CvUtil.FONT_RIGHT_JUSTIFY, self.X_NEXT, self.Y_NEXT, self.Z_TEXT, FontTypes.TITLE_FONT, WidgetTypes.WIDGET_GENERAL, 1, 0)
if(self.SCREEN_NUMBER > 0):
screen.setText(self.PREV_NAME, "Background", u"<font=4>" + localText.getText("TXT_KEY_SCREEN_PREV", ()).upper() + u"</font>", CvUtil.FONT_CENTER_JUSTIFY, self.X_PREV, self.Y_PREV, self.Z_TEXT, FontTypes.TITLE_FONT, WidgetTypes.WIDGET_GENERAL, 1, 0)
То есть, рисуем кнопки в заданные им координаты и проверяем условия: для продвижения экрана, что экран не последний, для возврата, что экран не первый.
Не забываем дать описатели ключам TXT_KEY_SCREEN_NEXT (Далее) и TXT_KEY_SCREEN_PREV (Назад) в текстовых xml-ках мода, для внятного отображения названия кнопок.
Строчку
self.setActivePlayer(gc.getGame().getActivePlayer())
теперь нужно выполнять только при условии
if (self.CHANGE_SCREEN_NUMBER == 0):
self.setActivePlayer(gc.getGame().getActivePlayer())
То есть вызывать эту функцию мы будем только, если экран открыт первый раз. В этой функции производится начальная инициализация выбора игрока. И поскольку мы хотим, что бы при переключении экранов у нас сохранялся выбор на предыдущем и можно было одновременно сменить несколько институтов, то нам не нужно вызывать эту функцию.
И, наконец, сбрасываем флаг переключения экранов:
self.CHANGE_SCREEN_NUMBER = 0
Собственно, здесь всё.
Теперь опишем функции переключения экранов (Вперёд/Назад)
def Next(self, inputClass):
screen = self.getScreen()
self.SCREEN_NUMBER = self.SCREEN_NUMBER + 1
self.CHANGE_SCREEN_NUMBER = 1
self.interfaceScreen()
return 0
def Previous(self, inputClass):
screen = self.getScreen()
self.SCREEN_NUMBER = self.SCREEN_NUMBER - 1
self.CHANGE_SCREEN_NUMBER = 1
self.interfaceScreen()
return 0
Первая увеличивает, вторая уменьшает номер экрана. После чего выставляется флаг переключения и вызывается перерисовка экрана.
Всё, сохраняем наш файлик, запускаем, проверяем :)
Теперь можно описать любое количество категорий институтов власти в XML и свободно использовать их в игре.
Аналогично можно переделать под свои нужды и любой экран или другой элемент интерфейса.
С уважением, Волод.