Arcade Türü Oyun Programlamayı
ve Bilgisayar Bilimleri ÖğreninChapter 10: Kontrolcüler ve Grafikler
Klavye, fare veya bir oyun kolu kullanarak nesneleri nasıl hareket ettirebiliriz?
10.1 Giriş
Şimdiye kadar, öğeleri ekranda nasıl oynatacağımızı gördük fakat onlarla etkileşime geçmeyi göstermedik. Fareyi, klavyeyi ve oyun kolunu nasıl okuyup kullanacağız? Neyse ki bu oldukça kolay.
10.2 Bir fonksiyonla kod çizdirmek
Başlamak için, ekranın etrafında hareket eden bir nesne greklidir. Bunu yapmanın en iyi yolu x ve y koordinatları alıp daha sonra nesneyi o bölgeye çizen bir fonksiyon yazmaktır.
Fonksiyona x ve y değerlerini vermek için, onları birer parametre olarak aktarıyoruz. Örneğin, alttaki kod bir kardan adam çiziyor.
def draw_snowman(screen, x, y): pygame.draw.ellipse(screen, WHITE,[35+x, 0+y, 25, 25]) pygame.draw.ellipse(screen, WHITE,[23+x, 20+y, 50, 50]) pygame.draw.ellipse(screen, WHITE,[0+x, 65+y, 100, 100])
Sonra, ana program döngüsünde, birden çok kardan adam çizilebilir:
# Sol üstte kardan adam draw_snowman(screen,10,10) # Sağ üstte kardan adam draw_snowman(screen,300,10) # Sol altta kardan adam draw_snowman(screen,10,300) |
Çalışan kodun tamamı için buraya tıklayın.
Bir ihtimal, bir önceki labda havalı bir şey çizen kod yazmış olabilirsiniz. Fakat, bunu nasıl bir fonksiyon haline getireceğiz? Örneğin, burada çöp adam çizen bir kod var:
# Kafa pygame.draw.ellipse(screen,black,[96,83,10,10],0) # Bacaklar pygame.draw.line(screen,black,[100,100],[105,110],2) pygame.draw.line(screen,black,[100,100],[95,110],2) # Gövde pygame.draw.line(screen,red,[100,100],[100,90],2) # Kollar pygame.draw.line(screen,red,[100,90],[104,100],2) pygame.draw.line(screen,red,[100,90],[96,100],2) |
Bu kod oldukça kolay bir şekilde bir fonksiyonun içerisine koyulabilir:
def draw_stick_figure(screen,x,y): # Kafa pygame.draw.ellipse(screen,black,[96,83,10,10],0) # Bacaklar pygame.draw.line(screen,black,[100,100],[105,110],2) pygame.draw.line(screen,black,[100,100],[95,110],2) # Gövde pygame.draw.line(screen,red,[100,100],[100,90],2) # Kollar pygame.draw.line(screen,red,[100,90],[104,100],2) pygame.draw.line(screen,red,[100,90],[96,100],2)
Fonksiyonu kodun içerisinde renklerin tanımından hemen sonrasına koyun. Ardından, orijinal kodun olduğu ünlü yerden fonksiyonu çağırın. Muhtemelen bunu açıklamanın en iyi yolu bu işi bir resim aracılığıyla yapmaktır:
Bu kod x ve y koordinatlarını içermez, yani her zaman tamamen aynı noktaya çöp adam çizecektir. Pek kullanışlı değildir. İçerisine x ve y koordinatlarını ekleyebiliriz:
def draw_stick_figure(screen,x,y): # Kafa pygame.draw.ellipse(screen,black,[96+x,83+y,10,10],0) # Bacaklar pygame.draw.line(screen,black,[100+x,100+y],[105+x,110+y],2) pygame.draw.line(screen,black,[100+x,100+y],[95+x,110+y],2) # Gövde pygame.draw.line(screen,red,[100+x,100+y],[100+x,90+y],2) # Kollar pygame.draw.line(screen,red,[100+x,90+y],[104+x,100+y],2) pygame.draw.line(screen,red,[100+x,90+y],[96+x,100+y],2)
Fakat sorun şeklin zaten orijinden belirli bir mesafede çizilmesidir. Program orijini (0, 0) kabul eder ve çöp adamı aşağı ve sağa 100 piksel uzaklığa çizer. Alttaki koda bakın:
Fonksiyona x ve y'nin eklenmesiyle, çöp adamın orijinini bu miktarda kaydırıyoruz. Mesela, eğer fonksiyonu şu şekilde çağırırsak:
draw_stick_figure(screen,50,50)
Bu kod çöp adamı (50, 50) noktasına koymuyor. Bu, orijini alta ve sağa 50 piksel kaydırıyor. Bizim çöp adamımız zaten (100, 100) civarında olduğu için, orijin kaydırmasıyla birlikte bu miktar (150, 150)'ye kayıyor. Fonksiyonun istediği yere şeklin çizilmesi için nasıl bir düzeltme yapabiliriz?
En küçük x ve yine en küçük y değerlerini bulun. Daha sonra hem x hem de y değerlerinin ikisini de fonksiyonun içerisindeki değerlerden çıkartın. Yükseklik ve genişlik değerlerini bozmayın. Burada en küçük x ve y değerlerini çıkarttığımız bir örnek var:
def draw_stick_figure(screen,x,y): # Kafa pygame.draw.ellipse(screen,black,[96-95+x,83-83+y,10,10],0) # Bacaklar pygame.draw.line(screen,black,[100-95+x,100-83+y],[105-95+x,110-83+y],2) pygame.draw.line(screen,black,[100-95+x,100-83+y],[95-95+x,110-83+y],2) # Gövde pygame.draw.line(screen,red,[100-95+x,100-83+y],[100-95+x,90-83+y],2) # Kollar pygame.draw.line(screen,red,[100-95+x,90-83+y],[104-95+x,100-83+y],2) pygame.draw.line(screen,red,[100-95+x,90-83+y],[96-95+x,100-83+y],2)
Ya da, programı daha basit bir hale getirip, çıkarmayı kendiniz yapın:
def draw_stick_figure(screen,x,y): # Kafa pygame.draw.ellipse(screen,black,[1+x,y,10,10],0) # Bacaklar pygame.draw.line(screen,black,[5+x,17+y],[10+x,27+y],2) pygame.draw.line(screen,black,[5+x,17+y],[x,27+y],2) # Gövde pygame.draw.line(screen,red,[5+x,17+y],[5+x,7+y],2) # Kollar pygame.draw.line(screen,red,[5+x,7+y],[9+x,17+y],2) pygame.draw.line(screen,red,[5+x,7+y],[1+x,17+y],2)
10.3 Fare
Çok iyi, artık bir nesneyi belirli koordinatlara çizdirecek bir fonksiyonu nasıl yazacağımızı biliyoruz. Bu koordinatlara nasıl ulaşabiliriz? En basit yol bunu fare ile yapmaktır. Koordinatları almak tek satırlık bir kodtan ibarettir:
pos = pygame.mouse.get_pos()
Püf noktası ise koordinatların bir liste veya daha kesin olarak söylemek gerekirse, değiştirilemez bir demet şeklinde gelmesidir. Hem x hem de y değerleri aynı değişkende saklanır. Yani eğer print( pos ) dersek alacağımız sonuç:
pos değişkeni iki sayıdan oluşan bir demettir. x koordinatı dizinin 0 numaralı indisinde ve y koordinatı da 1 numaralı indisinde yer alır. Bunlar, kolaylıkla alınabilir ve öğeyi çizen fonksiyona aktarılabilir:
# Oyun mantığı pos = pygame.mouse.get_pos() x = pos[0] y = pos[1] # Çizim bölümü draw_stick_figure(screen, x, y)
Farenin konumunun alınması ana program döngüsünde "oyun mantığı" kısmına gitmelidir. Fonksiyonun çağırılması ise ana program döngüsünün "çizim" kısmında yer almalıdır.
Bunun tek sorunu fare işaretçisinin çöp adamın direk üzerine çizilmesidir,
bu çöp adamın görülmesini engeller:
Alttaki kod ana döngüden hemen önce kullanılarak fare işaretçisi gizlenebilir:
# Fare imlecini gizle pygame.mouse.set_visible(0)
Çalışan bir örneğin tamamı şurada bulunabilir: move_mouse.py
10.4 Klavye
Klavye ile kontrol etmek biraz daha karmaşıktır. Fareden x ve y'yi direk alamayız. İhtiyacımız olan:
- Başlangıç x ve y değerlerini oluşturmak.
- Ok tuşuna basıldığında uygulanacak bir "hız" ("velocity") belirlemek (keydown)
- Ok tuşu bırakıldığında "hız"ı ("velocity") sıfırlamak (keyup)
- x ve y'yi hıza bağlı olarak her bir frame'de ayarlamak
Karışık görünüyor, fakat tıpkı daha önce yaptığımız hareketli dikdördgen gibi, istisnası ise hızın klavye tarafından kontrol edilmesi.
Başlangıç olarak, ana döngüden önce konum ve hız vektörü belirlenmiştir:
# Frame başına piksel cinsinden hız x_speed=0 y_speed=0 # Varsayılan konum x_coord=10 y_coord=10
Programın ana döngüsünün içerisimnde, olayların işlenmesi (event processing) değiştirilmiştir. Sadece çıkış olayına bakılmak yerine programın klavye olaylarına da bakmaya ihtiyacı vardır. Bu event'lar kullanıcı herhangi bir tuşa her bastığında meydana gelir. Başka bir event ise kullanıcı basılı tuşu bıraktığında üretilir. Kullanıcı bir tuşa bastığında, hız vektörü 3 veya -3 olarak ayarlanır. Kullanıcı tuşu bıraktığında, hız vektörü sıfır olarak geri atanır.
Son olarak, nesnenin koordinatları vektör tarafından ayarlanır ve sonra nesne çizilir. Alttaki kodu inceleyin:
for event in pygame.event.get(): if event.type == pygame.QUIT: done=True # Kullanıcı bir tuşa bastığında if event.type == pygame.KEYDOWN: # Bunun bir yön tuşu olup olmadığını belirle. # Eğer yön tuşuysa, hızı ayarla. if event.key == pygame.K_LEFT: x_speed=-3 if event.key == pygame.K_RIGHT: x_speed=3 if event.key == pygame.K_UP: y_speed=-3 if event.key == pygame.K_DOWN: y_speed=3 # Kullanıcı basılı tuşu bıraktığında if event.type == pygame.KEYUP: # Eğer bir yön tuşuysa, vektörü tekrar sıfır yap if event.key == pygame.K_LEFT: x_speed=0 if event.key == pygame.K_RIGHT: x_speed=0 if event.key == pygame.K_UP: y_speed=0 if event.key == pygame.K_DOWN: y_speed=0 # Hız vektörüne göre nesneyi hareket ettir. x_coord=x_coord+x_speed y_coord=y_coord+y_speed draw_background(screen) # Öğeyi yeni koordinatlara çiz. draw_item(screen,x_coord,y_coord)
Örneği tamamı için: move_keyboard.py dosyasına bakın. Bu örneğin öğenin ekranın kenarlarından dışarı çıkmasını engellemediğine dikkat edin. Bunu yapabilmek için, oyun mantığı kısmında, if yapılarından oluşan bir set x_coord ve y_coord değerlerini kontrol etmek için kullanılmalıdır. Eğer onlar ekranın sınırları dışarısındaysa, kodları kenara gelecek şekilde sıfırlamalıdır. Bunun için gereken kod, okuyucuya alıştırma olarak bırakılmıştır.
10.5 Oyun Kumandası
Oyun kumandaları farklı bir takım kodlar gerektirmektedir, fakat fikir hala basittir.
Başlamak için, bilgisayarınızda bir oyun kumandası (joystick) bağlı olup olmadığını kontrol edin ve onu kullanmadan önce başlatın. Bu sadece bir kere yapılmalıdır, onu ana program döngüsünden önce yapın:
# Varsayılan konum x_coord=10 y_coord=10 # Bilgisayarın kaç tane oyun kolu olduğunu say joystick_count=pygame.joystick.get_count() if joystick_count == 0: # Oyun kolu yok! print ("Hata, hiçbir oyun kolu bulamadım.") else: # Oyun kolu #0'ı kullan ve onu başlat my_joystick = pygame.joystick.Joystick(0) my_joystick.init()
A joystick will return two floating point values. If the joystick is perfectly centered it will return (0,0). If the joystick is fully up and to the left it will return (-1,-1). If the joystick is down and to the right it will return (1,1). If the joystick is somewhere in between, values are scaled accordingly. Oyun kolu iki ondalıklı sayı döndürecektir. Oyun kolu mükemmel biçimde ayarlandıysa merkezde (0, 0) döndürür.
Merkez | (0,0) | Sol Yukarı | (-1,-1) | ||
Yukarı | (0,-1) | Sağa Yukarı | (1,-1) | ||
Sağa | (1,0) | Sağ Aşağı | (1,1) | ||
Sağa | (0,1) | Sola | (-1,0) |
Ana program döngüsünün içerisinde, oyun kolundan dönen değerler bir nesnenin ne kadar uzağa hareket edeceğine bağlı olarak çarpılabilir. Alttaki kodta yer alan durumda, oyun kolunun bir yöne tamamen hareket etmesi onu frame başına 10 piksel hareket ettirecektir çünkü oyun kolu değerleri 10 ile çarpılmaktadır.
# Bir oyun kolu olduğu müddetçe if joystick_count != 0: # Bu oyun kumandasının eksen pozisyonunu alır # -1.0 ile +1.0 arasında bir sayı döndürür horiz_axis_pos= my_joystick.get_axis(0) vert_axis_pos= my_joystick.get_axis(1) # Eksene göre x'i hareket ettir. Hareketi hızlandırmak için 10 ile çarpıyoruz. x_coord=int(x_coord+horiz_axis_pos*10) y_coord=int(y_coord+vert_axis_pos*10) draw_stick_figure(screen) # Düzenli koordinatlarda öğeyi çiz draw_item(screen,x_coord,y_coord)
Örneğin tam hali için şu dosyaya bakın: move_game_controller.py.
Kontrolcüler birçok joystick, buton ve hatta anahtarlara sahiptir. Aşağıda bir
örnek program ve kontrolcünün yaptığı her şeyi ekrana yazdıran ekran görüntüsü
yer alıyor. Oyun kontrolcülerin program başlamadan önce takılması gerektiğine dikkat edin,
ya da program kontrolcüyü belirleyemez.
""" Sample Python/Pygame Programs Simpson College Computer Science http://programarcadegames.com/ http://simpson.edu/computer-science/ Show everything we can pull off the joystick """ import pygame # Define some colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) class TextPrint(object): """ This is a simple class that will help us print to the screen It has nothing to do with the joysticks, just outputting the information. """ def __init__(self): """ Constructor """ self.reset() self.x_pos = 10 self.y_pos = 10 self.font = pygame.font.Font(None, 20) def print(self, my_screen, text_string): """ Draw text onto the screen. """ text_bitmap = self.font.render(text_string, True, BLACK) my_screen.blit(text_bitmap, [self.x_pos, self.y_pos]) self.y_pos += self.line_height def reset(self): """ Reset text to the top of the screen. """ self.x_pos = 10 self.y_pos = 10 self.line_height = 15 def indent(self): """ Indent the next line of text """ self.x_pos += 10 def unindent(self): """ Unindent the next line of text """ self.x_pos -= 10 pygame.init() # Set the width and height of the screen [width,height] size = [500, 700] screen = pygame.display.set_mode(size) pygame.display.set_caption("My Game") # Loop until the user clicks the close button. done = False # Used to manage how fast the screen updates clock = pygame.time.Clock() # Initialize the joysticks pygame.joystick.init() # Get ready to print textPrint = TextPrint() # -------- Main Program Loop ----------- while not done: # EVENT PROCESSING STEP for event in pygame.event.get(): if event.type == pygame.QUIT: done = True # Possible joystick actions: JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN # JOYBUTTONUP JOYHATMOTION if event.type == pygame.JOYBUTTONDOWN: print("Joystick button pressed.") if event.type == pygame.JOYBUTTONUP: print("Joystick button released.") # DRAWING STEP # First, clear the screen to white. Don't put other drawing commands # above this, or they will be erased with this command. screen.fill(WHITE) textPrint.reset() # Get count of joysticks joystick_count = pygame.joystick.get_count() textPrint.print(screen, "Number of joysticks: {}".format(joystick_count)) textPrint.indent() # For each joystick: for i in range(joystick_count): joystick = pygame.joystick.Joystick(i) joystick.init() textPrint.print(screen, "Joystick {}".format(i)) textPrint.indent() # Get the name from the OS for the controller/joystick name = joystick.get_name() textPrint.print(screen, "Joystick name: {}".format(name)) # Usually axis run in pairs, up/down for one, and left/right for # the other. axes = joystick.get_numaxes() textPrint.print(screen, "Number of axes: {}".format(axes)) textPrint.indent() for i in range(axes): axis = joystick.get_axis(i) textPrint.print(screen, "Axis {} value: {:>6.3f}".format(i, axis)) textPrint.unindent() buttons = joystick.get_numbuttons() textPrint.print(screen, "Number of buttons: {}".format(buttons)) textPrint.indent() for i in range(buttons): button = joystick.get_button(i) textPrint.print(screen, "Button {:>2} value: {}".format(i, button)) textPrint.unindent() # Hat switch. All or nothing for direction, not like joysticks. # Value comes back in an array. hats = joystick.get_numhats() textPrint.print(screen, "Number of hats: {}".format(hats)) textPrint.indent() for i in range(hats): hat = joystick.get_hat(i) textPrint.print(screen, "Hat {} value: {}".format(i, str(hat))) textPrint.unindent() textPrint.unindent() # ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT # Go ahead and update the screen with what we've drawn. pygame.display.flip() # Limit to 60 frames per second clock.tick(60) # Close the window and quit. # If you forget this line, the program will 'hang' # on exit if running from IDLE. pygame.quit()
10.6 Tekrar
Çoktan seçmeli bir test için burayı tıklayın.
You are not logged in. Log in here and track your progress.
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin