Arcade Türü Oyun Programlamayı ve Bilgisayar Bilimleri Öğrenin

Arcade Türü Oyun Programlamayı
ve Bilgisayar Bilimleri Öğrenin

Chapter 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

Video: Bir fonksiyon ile çizim

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)
fig.snowmen
Figure 10.1: Fonksiyon tarafından çizilen kardanadamlar

Video: Hazırda bulunan bir kodu fonksiyona çevirmek

Ç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)
fig.stick_figure
Figure 10.2: Çöp Adam

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:

fig.making_a_function
Figure 10.3: Fonksiyon Haline Getirmek

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:

fig.stick_figure_2
Figure 10.4: Çöp Adam

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?

fig.code_example
Figure 10.5: Kod Örneği

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

Video: Fare ile hareket

Ç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ç:

fig.coordinates
Figure 10.6: Koordinatlar

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

Video: Klavye ile hareket

Klavye ile kontrol etmek biraz daha karmaşıktır. Fareden x ve y'yi direk alamayız. İhtiyacımız olan:

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.
joystick_calls.png

"""
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.