20 Şubat 2012 Pazartesi

Pygame (1)

Bir önceki derste de söylediğim gibi bu derste görsel bir şeyler yapmaya başlayacağız. Bunun için pygame kütüphanesini kullanacağız.

Nedir bu Pygame?
Pygame bir grafik kütüphanesidir. Sakın bunu grafik motoru ile karıştırmayın, aynı şey değil. Pygame de Python gibi ücretsiz, açık kaynak kodlu bir yazılımdır. Temelde yaptığı şey kare, çizgi, yuvarlak gibi basit şekilleri çizmek. Sakın bunu azımsamayın! Pek çoğunuzun ismini sık sık duyduğu Metin 2 adlı mmorpg'nin grafikleri pygame ile yapıldı. Gerçi pygame ile üç boyutlu bir oyun yapmak  biraz abartıya kaçıyor ama siz de hemen hemen her türlü iki boyutlu oyunu rahatlıkla yapabilirsiniz. Daha fazla örnek isterseniz www.pygame.org sitesini inceleyebilirsiniz.

Kurulum
Sitede downloads'a tıklarsanız bir çok pygame sürümü olduğunu göreceksiniz. Siz Python versiyonunuza uygun olanını indirin. Yalnız dikkat edin, 64 bit pygame sürümleri sadece Python 2.7 için mevcut. Eğer bilgisayarınızda Python 3.xx 64 bit kurulu ise onu kaldırıp 32 bit yüklemeniz gerekecek. Doğru sürümü indirdikten sonra kurulum sırasında Python'un kurulu olduğu dosyayı göstermeniz lazım. Genelde otomatik olarak tespit ediliyor ama, onun için çok fazla uğraşmanıza gerek yok.

Her şeyi kurduysanız IDLE'ı açın ve import pygame yazıp enter'a basın. Eğer Python herhangi bir şey yazmazsa her şeyi doğru yapmışsınızdır. Yok eğer bir hata verirse demek ki bir yerde yanlışlık var.

İlk Adımlar
Buraya kadar geldiyseniz başlamaya hazırsınızdır. O halde hemen 800x600 bir ekran yapalım:

import pygame
pygame.init()
pencere = pygame.display.set_mode((800, 600))
pygame.display.update()

Bunları yazarsanız karşınıza siyah bir pencere çıkacak. Burada şunu yaptık: önce pygame modülünü çağırdık, sonra pygame.init() fonksiyonu ile her şeyi varsayılan değerlere ayarladık (bu fonksiyonun tam olarak ne yaptığını bilmiyorum ama mutlaka yazılması lazım). Sonra penceremizi tanımladık. Onu da önce pygame modülünden display adlı bir modülün set_mode() diye bir fonksiyonunu çağırarak. Adından da anlaşıldığı gibi bu fonksiyon ekran ayarını yapıyor. Dikkat ettiyseniz orada çift parantez kullandım. Sebebi basit, set_mode() fonksiyonu birden çok değer alıyor (çözünürlük, derinlik, pencere kenarlarının olup olmaması gibi şeyler, ayrıntılı bilgi için buraya bakabilirsiniz), geriye kalanlar yazılmadığı zaman pygame onları varsayılan olarak ayarlıyor. Eğer başka özellikleri ayarlamak isteseydik pygame.display.set_mode((800, 600), pygame.FULLSCREEN) gibi bir şeyler yazacaktık. Son olarak da update() fonksiyonu var. Aslında update() fonksiyonu argüman olarak kare ya da kareler listesi alır ve her çağrıldığında sadece o kareleri yeniler (örnek: pygame.display.update(kare1, kare2, kare3), ama eğer herhangi bir argüman vermezsek sahnedeki bütün objeleri yeniler. Bu aşamada herhangi bir argüman vermemize gerek yok.

Bu programı çalıştırdığınızda bir sorun fark etmiş olabilirsiniz, sanki çıkan pencere donup da hata veriyormuş gibi. Aslında ortada bir hata yok, sadece pygame'e ekranı çizdikten sonra ne yapması gerektiğini söylemedik, o kadar. En sona pygame.quit() yazarsanız sorun kalmayacaktır. Pencere bir anda çıkıp kapanacak. Bunu engellemek için şimdilik daha önce yaptığımız gibi input() fonksiyonunu kullanabilirsiniz.

Şimdi penceremize biraz renk ekleyelim. Herhangi bir yüzeyi, renk ile doldurmak için yüzey_adı.fill((renk_kodu)) fonksiyonunu kullanacağız. Hemen bir örnek yapalım:


import pygame
pygame.init()
pencere = pygame.display.set_mode((800, 600))
pencere.fill((255, 255, 255))
pygame.display.update()
pygame.quit()

Gördüğünüz gibi penceremizin arka planı artık beyaz. Renk kodu olarak yazdığımız sayılar size bir şey ifade etmiyorsa hemen açıklayayım. RGB kısaltmasını hepiniz duymuşsunuzdur, Red Green Blue'dan gelir. İşte yazdığımız ilk sayı kırmızıya, ikinci sayı yeşile, üçüncüsü de maviye denk gelir. 0 - 255 arasında istediğiniz bir sayı yazabilirsiniz, sayı büyüdükçe renk miktarı da artar.

Şimdi de ekrana bir kare çizdirelim.

import pygame
pygame.init()
pencere = pygame.display.set_mode((800, 600))
kare = pygame.Rect((50, 50), (100, 100))
pencere.fill((255, 255, 255))
pygame.draw.rect(pencere, (0, 255, 0), kare)
pygame.display.update()
pygame.quit()

Önce pygame.Rect((sol_üst_köşenin_koodrinatları), (karenin_boyutları)) fonksiyonu ile bir kare tanımladık. Burada dikkat etmeniz gereken şey koordinat sisteminin sol üst köşeden başlaması. Yani (0, 0) noktası sol üst köşe, y koordinatı aşağıya gittikçe artar, x koordinatı ise sola gittikçe artar. pencere.fill()'dan sonra da pygame.draw.rect(yüzey, (karenin_rengi), karenin_kendisi) fonksiyonu ile de kareyi pencere adlı yüzeye çizdirdik. pygame.draw.rect()'in pencere.fill()'dan sonra olması öncemli, yoksa ekrana önce kare çizilirdi, sonra da bütün ekran beyaza boyanırdı ve biz kareyi görmezdik.

Şimdi gelelim asıl önemli konuya, kareyi nasıl hareket ettireceğiz? Bunun için bir döngü yaratmalıyız ki python kareyi sadece bir kere çizdirip programı sonlandırmasın. Demek ki bir adet while döngüsüne ihtiyacımız var.

import pygame, time
pygame.init()
pencere = pygame.display.set_mode((800, 600))
kare = pygame.Rect((50, 50), (100, 100))
devam = 1
while devam == 1:
    pencere.fill((255, 255, 255))
    kare.right += 1
    pygame.draw.rect(pencere, (0, 255, 0), kare)
    pygame.display.update()
    time.sleep(0.025)

Kareyi hareket ettirmek için karenin sağ kenarının koordinatını her döngüde bir piksel arttırdım. Bunu gördüğünüz gibi kare.right ile yaptım. Aynı şekilde kare.left'i de kullanabilirdim. Ya da aşağı - yukarı hareket ettirmek için kare.top ya da kare.bottom'ı da kullanabilirdim. Fark ettiyseniz bir de time modülünü import edip time.sleep() fonksiyonunu kullandım. Bunu yapmamdaki amaç her döngü sonunda bilgisayarın 0.025 saniye duraklaması. Yoksa bilgisayar hem programı çalıştırabildiği kadar hızlı çalıştırır ve muhtemelen biz kareyi görene kadar pencere dışına çıkar hem de her bilgisayarda program işlemciye bağlı olarak farklı hızlarda çalışır. Yetmiyormuş gibi işlemci de gücünün 100%'ünü sadece bunun için harcar.

Kare hareket ediyor, güzel ama şimdi de yeni bir sorun daha çıktı ortaya: pencere düzgün kapanmıyor. Hemen çaresine bakalım...

import pygame, time, sys
pygame.init()
pencere = pygame.display.set_mode((800, 600))
kare = pygame.Rect((50, 50), (100, 100))
devam = 1
while devam == 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    pencere.fill((255, 255, 255))
    kare.right += 5
    pygame.draw.rect(pencere, (0, 255, 0), kare)
    pygame.display.update()
    time.sleep(0.025)

Önce sys diye bir modül import ettim, döngüye girdikten sonra da
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
diye bir şey ekledim. Artık çarpıya bastığınızda pencere sorunsuz bir şekilde kapanacaktır. IDLE şöyle bir şey diyebilir:
Traceback (most recent call last):
  File "C:/Users/GameOver/Desktop/pencere.py", line 15, in <module>
    sys.exit()
SystemExit
aldırmayın, hata falan yok. Şimdilik de bu yazdıklarımı boş verin, ileride ne olduğunu zaten anlayacaksınız.

Biraz beklerseniz karenin ekranın dışına çıktığını fark edeceksiniz. Bunu hemen önleyelim. Kare ekranın sınırına geldiğinde öteki tarafa gitsin.


import pygame, time, sys
pygame.init()
pencere = pygame.display.set_mode((800, 600))
kare = pygame.Rect((50, 50), (100, 100))
devam = 1
sola_git = True
while devam == 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    pencere.fill((255, 255, 255))
    if kare.right >= 800:
        sola_git = True
    if kare.left <= 0:
        sola_git = False
    if sola_git:
        kare.left-= 5
    else:
        kare.right += 5
    pygame.draw.rect(pencere, (0, 255, 0), kare)
    pygame.display.update()
    time.sleep(0.025)

Şimdi sola_git diye bir değişken tanımladım ve değerini True yaptım. while döngüsünün içine ise birkaç şart ekledim.

if kare.right >= 800:
    sola_git = True

Bunnu anlamı eğer karenin sağ tarafı ekranın genişliğini (yani x = 800'ü geçerse) geçerse sola_git değişkenini True yap.


if kare.left <= 0:
    sola_git = False

Bunun anlamı da eğer karenin solu ekranın soluna gelirse (yani x = 0'ı geçerse) sola_git değişkeni False. Bundan sonraki if ve elif şartları da sola_git değişkeninin True ya da False olmasına göre karenin x koordinatını 5 arttırır ya da 5 azaltır.

Gelelim bu dersin asıl önemli konusuna; kontroller. Hemen kodu yazayım, sonra gerekli açıklamaları yaparım.


import pygame, time, sys
pygame.init()
pencere = pygame.display.set_mode((800, 600))
kare = pygame.Rect((50, 50), (100, 100))
devam = 1
while devam == 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                kare.left -= 1
            if event.key == pygame.K_RIGHT:
                kare.right += 1
            if event.key == pygame.K_UP:
                kare.top -= 1
            if event.key == pygame.K_DOWN:
                kare.bottom += 1
             
    pencere.fill((255, 255, 255))
    pygame.draw.rect(pencere, (0, 255, 0), kare)
    pygame.display.update()
    time.sleep(0.025)

Burada az önceki koda yazdığım sola_git değişkenini ve ona bağlı olan her şeyi sildim, şimdilik ona ihtiyacımız yok. Onun yerine while döngüsünün başındaki for döngüsüne ekleme yaptım. Gördüğünüz gibi çıkış fonksiyonundan hemen sonra if event.type == pygame.KEYDOWN diye bir şey ekledim. Bu "eğer klavyeden bir tuş basılırsa" anlamına gelir. Daha sonra da if event.key == pygame.K_LEFT yazdım, bunun anlamı da "eğer basılan tuş sol olursa"dır. Aynı şeyi sağ, yukarı ve aşağı için yaptım. Klavyedeki bütün tuşlardan bu şekilde girdi alabilirsiniz. Bütün tuşların karşılıklarını buradan bulabilirsiniz.

Tabi yazdığımız bu oyunda karemiz çok yavaş hareket ediyor ve her hareketi için tuşu bırakıp tekrar basmamız gerekiyor. Basılı tutunca hareket etmesini sağlamanın birkaç yolu var. En basiti while döngüsünden önce pygame.key.set_repeat(zaman_aralığı, basılma_sayısı) satırını eklemek. Zaman aralığı dediğim mili saniye  cinsinden geçecek zaman, basılma_sayısı ise o zaman içinde tuşun kaç kere basıldığıdır. Böyle anlatınca anlaşılması biraz zor oluyor ama o satırı kodunuza ekleyip sayılarla biraz oynarsanız hemen anlarsınız. Kısaca karenin basar basmaz durmandan harekete başlaması için pygame.key.set_repeat(1, 1) gibi bir şey yazmanız yeterli olacaktır.

Son olarak da çok önemli bir konu olan çarpışmaya değinmek istiyorum. Başlangıç aşamasında sadece kareleri kullanacağımız için karelerin çarpışmasını anlatacağım sizlere. Pygame'in bunun için çok güzel bir fonksiyonu var: kare1.colliderect(kare2) . Eğer kare1 kare2'ye çarpıyorsa bu fonksiyon True değerini verecektir. Yok eğer çarpmıyorsa False değerini verir. Hemen bir örnek yapalım.

import pygame, time, sys
pygame.init()
pencere = pygame.display.set_mode((800, 600))
kare = pygame.Rect((50, 50), (100, 100))
kare2 = pygame.Rect((300, 50), (100, 100))
devam = 1
pygame.key.set_repeat(1, 1)
while devam == 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                kare.left -= 1
            if event.key == pygame.K_RIGHT:
                kare.right += 1
            if event.key == pygame.K_UP:
                kare.top -= 1
            if event.key == pygame.K_DOWN:
                kare.bottom += 1

    if kare.colliderect(kare2):
        if kare.right < kare2.left + 2 :
            kare.right = kare2.left
        elif kare.left > kare2.right - 2:
            kare.left = kare2.right
        if kare.top > kare2.bottom - 2:
            kare.top = kare2.bottom
        elif kare.bottom < kare2.top + 2:
            kare.bottom = kare2.top
    pencere.fill((255, 255, 255))
    pygame.draw.rect(pencere, (0, 255, 0), kare)
    pygame.draw.rect(pencere, (255, 0, 0), kare2)
    pygame.display.update()
    time.sleep(0.025)

Bu örnekte sadece if kare.colliderect(kare2) şartından sonrasını ekledim. Sanırım çok fazla açıklamaya gerek yok. Eğer kafanız karışırsa bir kağıda orijini sol üst köşe olan bir koordinat sistemi çizin ve yazdığım şartları hayal edin.

Bu ders için bu kadarı yeter sanırım. Sonraki derste basit bir uçak oyunu yapacağız, daha da sonra çeşitli grafikler, resimler, sesler ekleyeceğiz. Kendinize çok iyi bakın.


2 yorum:

  1. Bu yorum yazar tarafından silindi.

    YanıtlaSil
  2. Abi ellerine sağlık muhteşem olmuş başarılarının devamını dilerim (gerçi biraz geç oldu ama...)

    YanıtlaSil