Полное руководство по созданию рогаликов на Python+Libtcod, глава 1

Материал из Клуб любителей рогаликов
Версия от 13:04, 26 марта 2012; Sanja (обсуждение | вклад) (Небольшая правка)
Перейти к: навигация, поиск

Глава 1: Графика

Подготовка

Итак, пришло время закатать рукава и взяться на работу! Если вы ещё не скачали и установили Python 2.7 (более старая 2.6 тоже сойдёт), то сделайте это. На других версиях тоже можно работать, но скорее всего вы столкнётесь с конфликтами в коде (хотя их и не должно быть слишком много). (Примечание для пользователей Windows 7 x64: устанавливайте 32-битную версию, так как, по-видимому, 64-битный Python не дружит с libtcod). Затем скачайте libtcod (версия 1.5.1rc1) и распакуйте его куда-нибудь. Если вы пользователь Windows, то выбирайте версию для MinGW, так как версия для Visual Studio не работает в связке с Python.

Создайте папку для проекта. И в ней создайте пустой файл с любым именем, на ваш вкус, например, firstrl.py. Простейший способ использовать libtcod это скопировать следующие файлы в папку с проектом:

  • libtcodpy.py
  • libtcod-mingw.dll на Windows, libtcod.so на Linux
  • SDL.dll на Windows, SDLlib.so на Linux
  • Любой шрифт из папки data\fonts. Мы возьмём arial10x10.png.


Выбор редактора кода

Если вы только начали своё знакомство с Python, то знайте, что множество Python-программистов пользуются простейшими редакторами и запускают свои скрипты через консоль, чтобы видеть отладочную информацию. У них и в мыслях нет перейти на крутую IDE! Для Windows наилучший выбор это Notepad++. Большинство Linux-программистов уже пользуются каким-то редактором. Почти все эти программы позволяют настроить клавиши быстрого доступа (вроде клавиши F5), чтобы быстро запускать скрипты, не переключаясь на консоль.

Если вы хотите создать ярлык для Notepad++ с парой полезных штук для отладки или вы что-то пробовали и получили известную ошибку "module not found", то прочитайте этот совет.

И ещё одно короткое замечание для тех, кто пользуется IDLE. По всей видимости, она некорректно завершает работу libtcod, вылетая по окончании работы скрипта. Думаю, это скорее проблема IDLE, чем libtcod, поэтому на данный момент решения нет. Воспользуйтесь любым из приведённых выше советов, они прекрасно работают.


Showing the @ on screen

This first part will be a bit of a crash-course. The reason is that you need a few lines of boilerplate code that will initialize and handle the basics of a libtcod window. And though there are many options, we won't explain them all or this part will really start to drag out. Fortunately the code involved is not as much as in many other libraries!

First we import the library. The name libtcodpy is a bit funky (sorry Jice!) so we'll rename it to just libtcod.

<syntaxhighlight lang="python">import libtcodpy as libtcod</syntaxhighlight>


Then, a couple of important values. It's good practice to define special numbers that might get reused. Many people capitalize them to distinguish from variables that may change.


<syntaxhighlight lang="python">SCREEN_WIDTH = 80

SCREEN_HEIGHT = 50

LIMIT_FPS = 20</syntaxhighlight>


Now, something libtcod-specific: we're going to use a custom font! It's pretty easy. libtcod comes bundled with a few fonts that are usable right out of the box. Remember however that they can be in different formats, and you'll need to tell it about this. This one is "grayscale" and using the "tcod layout", most fonts are in this format and thus end with _gs_tc. If you wanna use a font with a different layout or make your own, the docs on the subject are really informative. You can worry about that at a later time though. Notice that the size of a font is automatically detected.


<syntaxhighlight lang="python">libtcod.console_set_custom_font('arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD)</syntaxhighlight>


This is probably the most important call, initializing the window. We're specifying its size, the title (change it now if you want to), and the last parameter tells it if it should be fullscreen or not.


<syntaxhighlight lang="python">libtcod.console_init_root(SCREEN_WIDTH, SCREEN_HEIGHT, 'python/libtcod tutorial', False)</syntaxhighlight>


For a real-time roguelike, you wanna limit the speed of the game (frames-per-second or FPS). If you want it to be turn-based, ignore this line. (This line will simply have no effect if your game is turn-based.)

<syntaxhighlight lang="python">libtcod.sys_set_fps(LIMIT_FPS)</syntaxhighlight>


Now the main loop. It will keep running the logic of your game as long as the window is not closed.


<syntaxhighlight lang="python">while not libtcod.console_is_window_closed():</syntaxhighlight>


For each iteration we'll want to print something useful to the window. If your game is turn-based each iteration is a turn; if it's real-time, each one is a frame. Here we're setting the text color to be white. There's a good list of colors you can use here, along with some info about mixing them and all that. The zero is the console we're printing to, in this case the screen; more on that later.


<syntaxhighlight lang="python"> libtcod.console_set_foreground_color(0, libtcod.white)</syntaxhighlight>


Don't forget the indentation at the beginning of the line, it's extra-important in Python. Make sure you don't mix tabs with spaces for indentation! This comes up often if you copy-and-paste code from the net, and you'll see an error telling you something about the indentation (that's a pretty big clue right there!). Choose one option and stick with it. In this tutorial we're using the 4-spaces convention, but tabs are easy to work with in many editors so they're a valid choice too.

Now print a string, left-aligned, to the coordinates (1,1). Once more the first zero specifies the console, which is the screen in this case. Can you guess what that string is? No, it doesn't move yet!


<syntaxhighlight lang="python"> libtcod.console_print_left(0, 1, 1, libtcod.BKGND_NONE, '@')</syntaxhighlight>


At the end of the main loop you'll always need to present the changes to the screen. This is called flushing the console and is done with the following line.


<syntaxhighlight lang="python"> libtcod.console_flush()</syntaxhighlight>


Ta-da! You're done. Run that code and give yourself a pat on the back!

Note that since we don't have any input handling code, the game may crash on exit (it won't process the OS's requests to close). Oops! Don't worry though, this problem will go away as soon as we add keyboard support.

Here's the complete code so far.


Moving around

That was pretty neat, huh? Now we're going to move around that @ with the keys!

First, we need to keep track of the player's position. We'll use these variables for that, and take the opportunity to initialize them to the center of the screen instead of the top-left corner. This can go just before the main loop.


<syntaxhighlight lang="python">playerx = SCREEN_WIDTH/2 playery = SCREEN_HEIGHT/2</syntaxhighlight>


There are functions to check for pressed keys. When that happens, just change the coordinates accordingly. Then, print the @ at those coordinates. We'll make a separate function to handle the keys.


<syntaxhighlight lang="python">def handle_keys():
   global playerx, playery
   
   #movement keys
   if libtcod.console_is_key_pressed(libtcod.KEY_UP):
       playery -= 1
       
   elif libtcod.console_is_key_pressed(libtcod.KEY_DOWN):
       playery += 1
       
   elif libtcod.console_is_key_pressed(libtcod.KEY_LEFT):
       playerx -= 1
       
   elif libtcod.console_is_key_pressed(libtcod.KEY_RIGHT):
playerx += 1</syntaxhighlight>


Done! These are the arrow keys, if you want to use other keys here's a reference (pay attention to the Python-specific notes).

While we're at it, why not include keys to toggle fullscreen mode, and exit the game? You can put this at the beginning of the function.


<syntaxhighlight lang="python">
   key = libtcod.console_check_for_keypress()
   if key.vk == libtcod.KEY_ENTER and key.lalt:
       #Alt+Enter: toggle fullscreen
       libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())
       
   elif key.vk == libtcod.KEY_ESCAPE:
return True #exit game</syntaxhighlight>


From now on, we'll show code for a real-time game with a green background, and code for a turn-based game with a blue background.

Notice a subtle difference here. The console_is_key_pressed function is useful for real-time games, since it checks if a key is pressed with no delays. console_check_for_keypress, on the other hand, treats the key like it's being typed. So after the first press, it will stop working for a fraction of a second. This is the same behavior you see when you type, otherwise pressing a key would result in you typing 3 or 4 letters! It's useful for all commands except movement, which you usually want to react as soon as possible with no delays, and continue for as long as you press the movement keys.

Now here's an important thing: you can use that first line to distinguish between real-time and turn-based gameplay! See, console_check_for_keypress won't block the game. But if you replace it with this line:

<syntaxhighlight lang="python"> key = libtcod.console_wait_for_keypress(True)</syntaxhighlight>


Then the game won't go on unless the player presses a key. So effectively you have a turn-based game now.

Now, the main loop needs to call this function in order for it to work. If the returned value is True, then we "break" from the main loop, ending the game. The inside of the main loop should now look like this:


<syntaxhighlight lang="python">
   libtcod.console_set_foreground_color(0, libtcod.white)
   libtcod.console_print_left(0, playerx, playery, libtcod.BKGND_NONE, '@')
   
   libtcod.console_flush()
   
   #handle keys and exit game if needed
   exit = handle_keys()
   if exit:
break</syntaxhighlight>


The reason why we draw stuff before handling key input is that, in a turn-based game, the first screen is shown before the first key is pressed (otherwise the first screen would be blank).

One more thing! If you try that, you'll see that moving you leave around a trail of little @'s. That's not what we want! We need to clear the character at the last position before moving to the new one, this can be done by simply printing a space there. Put this just before exit = handle_keys().


<syntaxhighlight lang="python"> libtcod.console_print_left(0, playerx, playery, libtcod.BKGND_NONE, ' ')</syntaxhighlight>


Here's a rundown of the whole code so far.

Go on to the next part.