28
Kas

Bu dersimizde simülasyonumuzu geliştirmeye devam ediyoruz.

Düşen topların bir engelle karşılasmasını sağlayacağız.

def sabit_cizgi_ekle(space):
    govde = pymunk.Body(pymunk.inf, pymunk.inf)
    govde.position = (300,200)
    l1 = pymunk.Segment(govde, (-200, 0), (200.0, 0.0), 5.0)
    l2 = pymunk.Segment(govde, (-200.0, 0), (-200.0, 50.0), 5.0)
    l3 = pymunk.Segment(govde, (200.0, 0), (200.0, 50.0), 5.0)

    space.add_static(l1, l2,l3)
    return l1,l2,l3

def to_pygame(p):
    """pymunk-pygame koordinat düzenlemesi yapar."""
    return int(p.x), int(-p.y+600)

def cizgileri_ciz(ekran, cizgiler):
    for cizgi in cizgiler:
        body = cizgi.body
        pv1 = body.position + cizgi.a.rotated(body.angle)
        pv2 = body.position + cizgi.b.rotated(body.angle)
        p1 = to_pygame(pv1)
        p2 = to_pygame(pv2)
        pygame.draw.line(ekran, THECOLORS["red"], p1, p2)

sabit_cizgi_ekle() fonksiyonuna yine space adında bir parametre veriyoruz. Fonksyionumuzda bir gövde tanımladık ve kütlesiyle momentini pymunk.inf değişkeniyle sonsuz yaptık. Bir nevi duvara monteleme işlemide denebilir sanırım… Gövdemizin pozisyonunu vektörel olarak 300×200 olarak belirledik. l1, l2, l3 değişkenlerine Segment nesnesi tanımladık. İlk parametreyle gövdesini diğer iki parametresini pygame.draw.line() da gördüğümüz gibi çizgi koordinatlarını belirledik. Son koordinat ise çizginin çapını belirliyor; daha doğrusu kalınlığı diyebiliriz…

Sabit olarak ekleyeceğimiz nesneleri Space sınıfımızın add_static() methodu ile ekliyoruz ve fonksiyonumuzda bu şekilde ekledikten sonra fonksiyonumuzun bu çizgileri döndürmesini sağladık.

cizgileri_ciz() fonksiyonuna, çizgileri çizeceği yüzeyi belirtmek için ekran parametresiyle; sabit_cizgi_ekle() fonksiyonundan dönen Segment nesnelerini grafik olarak çizdirmek için de cizgiler parametresini ekledik. Çizgileri bir for döngüsü yardımıyla tek tek pygame.draw.line() ile ekrana çizdirilmesini sağladık. pv1 ile pv2 de çizgilerin Space deki pozisyonlarını ayarladık. rotated(body.angle) da ise çizgilerin bir açısı varsa ona göre bir düzenleme yapılması sağlanıyor. Eğer bir çizgi 45 derece aksi yöne yatıksa Vektörde o kısımın değeri artış ya da azalış gösterecektir.

p1 ve p2 de ise önceki dersimizde pygame’e özgü koordinat çevirme işlemi yapılıyor. Bu çevrimi yapılmış koordinatlar çizginin simülasyondaki yerine göre çizilmesini sağlıyor.

#-*- coding: utf-8 -*-
import sys
import pygame
from pygame.color import *
import pymunk

#def top_ekle(space):
#def top_cizdir(ekran, top):
# Yer kaplamaması için bu şekilde gösterildi. Siz kodunuzu olduğu gibi bırakın.

def sabit_cizgi_ekle(space):
    govde = pymunk.Body(pymunk.inf, pymunk.inf)
    govde.position = (300,200)
    l1 = pymunk.Segment(govde, (-200, 0), (200.0, 0.0), 5.0)
    l2 = pymunk.Segment(govde, (-200.0, 0), (-200.0, 50.0), 5.0)
    l3 = pymunk.Segment(govde, (200.0, 0), (200.0, 50.0), 5.0)
    space.add_static(l1, l2,l3)
    return l1,l2,l3

def to_pygame(p):
    """pymunk-pygame koordinat düzenlemesi yapar."""
    return int(p.x), int(-p.y+600)

def cizgileri_ciz(ekran, cizgiler):
    for cizgi in cizgiler:
        body = cizgi.body
        pv1 = body.position + cizgi.a.rotated(body.angle)
        pv2 = body.position + cizgi.b.rotated(body.angle)
        print cizgi.a.rotated(1.0), cizgi.b.rotated(0.0)
        p1 = to_pygame(pv1)
        p2 = to_pygame(pv2)
        pygame.draw.line(ekran, THECOLORS["red"], p1, p2)

pygame.init()
ekran = pygame.display.set_mode((600, 600))
saat = pygame.time.Clock()

space = pymunk.Space()
space.gravity = (0.0, -900.0)

cizgiler = sabit_cizgi_ekle(space)
toplar = []

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            top_sekli = top_ekle(space)
            toplar.append(top_sekli)
            pass

    ekran.fill(THECOLORS["white"])
    for top in toplar:
        top_cizdir(ekran, top)
    cizgileri_ciz(ekran, cizgiler)
    space.step(1/60.0)
    pygame.display.flip()
    saat.tick(60)

Örnekteki kodda cizgiler değişkenine sabit çizgilerin listesini tutturduk ve oyun döngümüzde cizgileri_ciz() fonksiyonumuza bu değişkeni parametre olarak vererek çizgilerimizi ekrana çizilmesini sağladık. Şimdi bu kodu çalıştırdığınızda bir hata yapmadıysanız fare tıklamalarınızda tepsi gibi bir alana top doldurabilirsiniz.

Şimdi sabit duran bu çizgileri bir pervanenin kanadı gibi bir noktasından iğneleyelim. Bunun için sabit_cizgi_ekle() fonksiyonunun adını cizgi_ekle() olarak değiştiriyoruz ve

def cizgi_ekle(space):
    devir_govdesi = pymunk.Body()
    devir_govdesi.position = (300,200)
    govde = pymunk.Body(50, 10000)
    govde.position = (300,200)
    l1 = pymunk.Segment(govde, (-200, 0), (200.0, 0.0), 5.0)
    l2 = pymunk.Segment(govde, (-200.0, 0), (-200.0, 50.0), 5.0)
    devir_eklemi = pymunk.PinJoint(govde, devir_govdesi, (0,0), (0,0))
    space.add(l1, l2, govde, devir_eklemi)
    return l1,l2

bu şekilde bir kod ortaya çıkarıyoruz… İlk olarak devir_govdesi adında yeni bir Body nesnesi oluşturuyoruz ve diğer Body nesnemizle aynı pozisyona getiriyoruz. Çizgileri tutan gövdemiz artık sabit olmayacağı için 50 kütleli ve 10000 momente sahip oluyor. Sağ taraftaki l3 çizgisinide çıkarıyoruz.

devir_eklemi adında bir PinJoint nesnesi oluşturduk. Bunu bir çubuğun ortasına saplanmış iğne gibi düşünebilirsiniz. PinJoint sınıfının ilk iki parametresi a ve b noktaları olmak üzere koordinat verisi alır; diğer iki parametree farklı bir değer vermeyecekseniz yazmanıza gerek yoktur. Hakkında pek bilgim yok; onun için rasgele değerler vererek ne işe yaradığını çıkartabilirsiniz.

Space sınıfının add_static() methodu yerine de add() methodunu kullandığımıza dikkat edin…

Son olarak şu satırı

cizgiler = sabit_cizgi_ekle(space)

şu şekilde değiştiriyoruz:

cizgiler = cizgi_ekle(space)

Kodumuzu çalıştırıp fare sol tuşuyla top oluşturarak topun çizgilere temas etmesiyle çizgilerin pervane gibi döndüğünü göreceksiniz. Tabii ki; bunu bu şekilde bırakmayacağız. Pervane gibi dönmesini engelleyip kısıtlı olarak sağ-sol aşağı doğru eğilmesini sağlayacağız.

def cizgi_ekle(space):
    devir_govdesi = pymunk.Body()
    devir_govdesi.position = (300,200)
    sinirli_devir_govdesi = pymunk.Body()
    sinirli_devir_govdesi.position = (200,200)
    govde = pymunk.Body(50, 10000)
    govde.position = (300,200)
    l1 = pymunk.Segment(govde, (-200, 0), (200.0, 0.0), 5.0)
    l2 = pymunk.Segment(govde, (-200.0, 0), (-200.0, 50.0), 5.0)
    devir_eklemi = pymunk.PinJoint(govde, devir_govdesi, (0,0), (0,0))
    eklem_siniri = 25
    sinirli_devir_eklemi = pymunk.SlideJoint(govde, sinirli_devir_govdesi, (-100,0), (0,0), 0, eklem_siniri)
    space.add(l1, l2, govde, devir_eklemi, sinirli_devir_eklemi)
    return l1,l2

Fonksiyonumuz biraz daha gelişti… sinirli_devir_govdesi adında yeni bir Body nesnesi tanımladık ve 200×200 pozisyonuna ayarladık. sinirli_devir_eklemi adında ise SlideJoint nesnesi tanımladık. İlk iki parametre PinJoint sınıfındaki gibidir. Üç ve dördüncü parametrelerde PinJoint sınıfının son iki parametresiyle aynı işleve sahiptir. parametre adı anchor olduğundan en uygun anlamı çapa oluyor. Buna göre x koordinatı -100 olan yere çapa atılmış oluyor. Belgelendirme yeterli düzeyde olmadığından ancak bu kadarı çıktı malesef… Son parametre eklem_siniri ise çizginin ne kadar kaydırılılabileceğini(?) söylüyor. Buna da farklı değerler vererek pekiştirme yapabilirsiniz. Son olarakta Space nesnemizin add() methoduna SlideJoint nesnemizi ekliyoruz. Fonksiyonla işimiz burada bitiyor…

Önceki derste listeye eklenen topların ekranda olmamasına rağmen listede yer işgal ettiğini ve bunlarında programın ekrana çizmeye çalıştığını söylemiştik. Ekleyeceğimiz tek satır kod ile y koordinatı sıfırın altına düşen topları listeden çıkartacağız ve olası şişme ve performans sorununun önüne geçeceğiz.

for top in toplar:
    top_cizdir(ekran, top)

Oyun döngümüzdeki bu for döngüsüne

if top.body.position.y < 0: toplar.remove(top)

kodunu ekleyerek

for top in toplar:
    if top.body.position.y < 0: toplar.remove(top)
    top_cizdir(ekran, top)

Bu şekle getiriyoruz. Artık kodumuz sağlıklı bir şekilde çalışmalı. Kodumuzun son hali şu şekilde:

#-*- coding: utf-8 -*-
import sys
import pygame
from pygame.color import *
import pymunk

def top_ekle(space):
    kutle = 1
    yaricap = 24
    eylemsizlik = pymunk.moment_for_circle(kutle, 0, yaricap)
    govde = pymunk.Body(kutle, eylemsizlik)
    pos = pygame.mouse.get_pos()
    govde.position = pos[0], 600-pos[1]
    sekil = pymunk.Circle(govde, yaricap)
    space.add(govde, sekil)
    return sekil

def top_cizdir(ekran, top):
    p = int(top.body.position.x), 600-int(top.body.position.y)
    pygame.draw.circle(ekran, THECOLORS["blue"], p, int(top.radius), 0)

def cizgi_ekle(space):
    devir_govdesi = pymunk.Body()
    devir_govdesi.position = (300,200)
    sinirli_devir_govdesi = pymunk.Body()
    sinirli_devir_govdesi.position = (200,200)
    govde = pymunk.Body(50, 10000)
    govde.position = (300,200)
    l1 = pymunk.Segment(govde, (-200, 0), (200.0, 0.0), 5.0)
    l2 = pymunk.Segment(govde, (-200.0, 0), (-200.0, 50.0), 5.0)
    devir_eklemi = pymunk.PinJoint(govde, devir_govdesi, (0,0), (0,0))
    eklem_siniri = 25
    sinirli_devir_eklemi = pymunk.SlideJoint(govde, sinirli_devir_govdesi, (-100,0), (0,0), 0, eklem_siniri)
    space.add(l1, l2, govde, devir_eklemi, sinirli_devir_eklemi)
    return l1,l2

def to_pygame(p):
    return int(p.x), int(-p.y+600)

def cizgileri_ciz(ekran, cizgiler):
    for cizgi in cizgiler:
        body = cizgi.body
        pv1 = body.position + cizgi.a.rotated(body.angle)
        pv2 = body.position + cizgi.b.rotated(body.angle)
        p1 = to_pygame(pv1)
        p2 = to_pygame(pv2)
        pygame.draw.line(ekran, THECOLORS["red"], p1, p2)

pygame.init()
ekran = pygame.display.set_mode((600, 600))
saat = pygame.time.Clock()

space = pymunk.Space()
space.gravity = (0.0, -900.0)

cizgiler = cizgi_ekle(space)
toplar = []

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            top_sekli = top_ekle(space)
            toplar.append(top_sekli)
            pass

    ekran.fill(THECOLORS["white"])
    for top in toplar:
        if top.body.position.y < 0: toplar.remove(top)
        top_cizdir(ekran, top)
    cizgileri_ciz(ekran, cizgiler)

    space.step(1/60.0)
    pygame.display.flip()
    saat.tick(60)

Ne yazık ki pymunk hakkında pek fazla belge yok ve Kaynak altında verdiğim linkler ne kadar yeterli olur bilemiyorum, ama İngilizcesi yeterli olanlar sınıf isimlerinden vs. öğrenmekte zorluk çekmeye bilirler. Bende bu yazıyı yazarken bu modülü öğrendiğim için sorularınıza cevap veremezsem kusuruma bakmayın...

Kaynak:

http://code.google.com/p/pymunk/wiki/SlideAndPinJointsExample

http://pymunk.googlecode.com/svn/trunk/docs/api/index.html

Bu yazı Creative Commons-BY-SA ile lisanslanmıştır. Bu yazıyı ilk sahibini belirtmek ve aynı lisansla dağıtmak koşuluyla kullanabilirsiniz.

Benzer Yazılar:

  1. pymunk 2B Fizik Kütüphanesi – Bölüm 1
  2. Pygame ile Oyun Programlama – Sprite 1. Bölüm
26
Kas

pymunk kullanımı basit 2 boyutlu oyunlar için hazırlanmış 2B fizik motorudur. Bu modül chipmunk üzerine inşa edilmiştir. pymunk’u pygame, pyglet, PySFML gibi oyun kütüphaneleriyle kullanabileceğiniz gibi PyQt gibi grafik arayüz kütüphaneleriyle de kullanabilirsiniz.

pymunk modülünü öğrenirken pygame modülünden faydalanacağız…

pymunk ile daire, dikdörtgen ve çokgen şekilleri fizik kurallarına uydurabileceğimiz sınıflar ile eklem oluşturmak, engel oluşturmak gibi işlemler için de sınıflar mevcuttur.

pymunk modülünü; GNU/Linux kullanıcıları python-setuptools paketini depolarından kurduktan sonra “easy_install pymunk” ya da “easy_install pip” dedikten sonra “pip install pymunk” komutunu vererek kurabilir, Windows kullanıcıları ise buradan uygun buldukları setup dosyasını indirerek kurabilirler. Tabii GNU/Linux kullanıcıları komutları verirken yönetici yetkisi almalıdır. Yani komutunuzun başına “sudo” komutunu eklemelisiniz…

Kurulum tamamlandıktan sonra Python yorumlayıcısına

import pymunk

yazarak test edebilirsiniz. Eğer ImportError hatası almıyorsanız sorun yok demektir. O zaman motoru çalıştıralım :)

pymunk’ta fizik kurallarını uygulayacağımız bir alan oluşturmamız gereklidir. Bu işi Space() sınıfı ile yapıyoruz. Daha sonra gravity özelliği ile yer çekiminin uygulanacağı yönü belirliyoruz…

import sys
import pygame
from pygame.color import *
import pymunk

pygame.init()
ekran = pygame.display.set_mode((600, 600))
saat = pygame.time.Clock()

space = pymunk.Space()
space.gravity = (0.0, -900.0)

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            sys.exit()

    ekran.fill(THECOLORS["white"])
    space.step(1/60.0)
    pygame.display.flip()
    saat.tick(60)

Örnekte görüldüğü gibi space değişkenine Space sınıfımızı atadık ve gravity özelliğine (0.0, -900.0) olarak atadık. İster bu şekilde yazalım, ister pymunk.Vec2d(0.0, -900.0) yazalım fark etmez. Sınıfımız doğru şekilde girilen veriyi kendisi Vec2d vektör nesnesine dönüştürecektir.

gravity özelliğine girilen ilk değer X koordinatını, ikincisi ise Y koordinatını temsil eder. Artı değer verilirse X için sağa, Y için yukarı doğru yer çekimi oluşur. Eksi değer girilirse tersi yönlere yer çekimi oluşur. Tabii yer çekimine maruz kalan nesnelerin vektörel değer girdiğimizden pygame koordinat tarzını ona göre ayarlamak gerekir.

Oyun döngümüzde 60 fps olarak ayarladığımız döngü sayısını fizik hesaplama süresiyle eşit tutuyoruz. step() methoduna girdiğimiz değerde oyun döngüsüyle eşit hesaplama yapmasını sağlıyor. Burada amaç simülasyonun uyumlu olmasını sağlamaktır.

Alanımızı oluşturduğumuza göre şimdi geometrik şekilleri çizdirmeye çalışabiliriz…

İlk olarak fare imleciyle ekrana tıkladığımızda bir daire çizdirelim ve fizik kurallarına göre yer çekimiyle aşağı doğru hareket etmesini sağlayalım…

def top_ekle(space):
    kutle = 1
    yaricap = 24
    eylemsizlik = pymunk.moment_for_circle(kutle, 0, yaricap)
    govde = pymunk.Body(kutle, eylemsizlik)
    pos = pygame.mouse.get_pos()
    govde.position = pos[0], 600-pos[1]
    sekil = pymunk.Circle(govde, yaricap)
    space.add(govde, sekil)
    return sekil

def top_cizdir(ekran, top):
    p = int(top.body.position.x), 600-int(top.body.position.y)
    pygame.draw.circle(ekran, THECOLORS["blue"], p, int(top.radius), 0)

top_ekle ve top_cizdir adında iki fonksiyon yazdık. top_ekle fonksiyonundan başlayalım:

Çizdireceğimiz şekle göre bir moment hesabı yapılması gerekiyor. Daire çizdireceğimiz zaman moment_for_circle fonksiyonunu dairenin moment hesabını yapmak için kullanırız. Değişken adlarından anlayacağınız gibi ilk parametresine kütle değerini, üçüncü parametresine de dairenin yarıçapını giriyoruz. 0 olan ikinci parametre ise dairenin iç yarıçapını temsil ediyor. Yani delikli para gibi bir şey düşünüyorsanız ikinci parametreye yarıçap verebilirsiniz.

Oluşturacağımız şekiller Body adında bir gövdeye ait olmalıdırlar. Body nesnemizi oluştururken şeklimizin momentinden ve kütlesinden haberdar olmasını sağlıyoruz. Sonra da çizileceği an gelince fare imleci pozisyonuna göre, ama y ekseni ekran yüksekliğinden çıkartılarak çizilmesi sağlanıyor. Çünkü matematikten hatırlarsanız y ekseni aşağı doğru eksilere inerken pygame de aşağı doğru artar. Bunu tersine çevirmek için böyle bir şey yapıyoruz.

Body nesnemiz ve yarıçap bilgisiyle bir Circle nesnesi oluşturuyoruz ve space parametresinden yararlanarak Space nesnemize gövdemizi ve şeklimizi ekliyoruz. Fonksiyonumuz bize Circle nesnemizi döndürüyor…

top_cizdir fonksiyonumuza ise ekran ve top parametrelerini almasını sağlıyoruz. p değişkeninde pygame’in anlayacağı koordinat verisini ayarlıyoruz ve draw.circle() fonksiyonumuz ile ekrana mavi renkli ve çapı 48 pixel olan bir daire çizilmesini sağlıyoruz.

Şimdi de ana kodumuza ufak eklemeler yaparak tam kodu yazalım.

import sys
import pygame
from pygame.color import *
import pymunk

def top_ekle(space):
    kutle = 1
    yaricap = 24
    eylemsizlik = pymunk.moment_for_circle(kutle, 0, yaricap)
    govde = pymunk.Body(kutle, eylemsizlik)
    pos = pygame.mouse.get_pos()
    govde.position = pos[0], 600-pos[1]
    sekil = pymunk.Circle(govde, yaricap)
    space.add(govde, sekil)
    return sekil

def top_cizdir(ekran, top):
    p = int(top.body.position.x), 600-int(top.body.position.y)
    pygame.draw.circle(ekran, THECOLORS["blue"], p, int(top.radius), 0)

pygame.init()
ekran = pygame.display.set_mode((600, 600))
saat = pygame.time.Clock()

space = pymunk.Space()
space.gravity = (0.0, -900.0)

toplar = []

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            top_sekli = top_ekle(space)
            toplar.append(top_sekli)

    ekran.fill(THECOLORS["white"])
    for top in toplar:
        top_cizdir(ekran, top)
    space.step(1/60.0)
    pygame.display.flip()
    saat.tick(60)

Evet toplar adında boş bir liste oluşturduk ve ekranda fare sol tuşuyla bastığımızda şeklimizin oluşmasını ve toplar listesine eklenmesini sağladık. Oyun döngümüzde ise listeye eklenen her topun sırayla çizilmesini sağladık. Kodu çalıştırıp topları sol fare tuşuyla oluşturmaya başladıktan sonra subliminal mesaj gibi mavi topların belirip kaybolduğunu görebilirsiniz. Bu durum ya modüllerden kaynaklanan bir durum ya da listeye eklenen ama ekrandan çıkmış toplarında çizilmeye çalışmasından kaynaklı olabilir. Bir sonraki devam dersimizde bu konuyuda halletmeye çalışacağız…

Bu yazı Creative Commons-BY-SA ile lisanslanmıştır. Bu yazıyı ilk sahibini belirtmek ve aynı lisansla dağıtmak koşuluyla kullanabilirsiniz.

Benzer Yazılar:

  1. Pygame ile Oyun Programlama – Sprite 1. Bölüm
  2. Pygame ile Oyun Programlama – Sprite 2. Bölüm
  3. Pygame ile Oyun Programlama – Sprite 3. Bölüm