22 Haziran 2013 Cumartesi

Vektörler

Şuana kadar cismimizi sabit yönlerde hareket ettirdik.Genelde hedef pozisyon belli değildi.Peki ya belli bir yönde hareket ettirmemiz gerekiyorsa ne yapacağız?Örneğin karakterimizin silahla önüne çıkan yaratıklara ateş ettiğini düşünelim.Kuşbakışı düşündüğümüzde merminin belli bir yönde gitmesi gerekiyor.Böyle bir durumda başlangıç koordinatıyla birlikte hedef koordinatını da bilmemiz lazım. Bu gibi durumlarda vektörlerden faydalanacağız.
pygame.math modülü içerisinde Vec2 isimli bir class var.Ama halen geliştirilme aşamasında.Bu yüzden kendi vektör sınıfımızı yazarak işe başlayalım.Sınıf kodumuz, python öğrenmeye yeni başlayanlar için biraz karışık gelebilir.Gerekli linkleri yazı içerisinde paylaşacağım.
#-*- coding:utf-8 -*-
#!/usr/bin/env python
import math

class Vector2(object):
 
 def __init__(self, x=0.0,y=0.0):
  self.x = x
  self.y = y

 def __str__(self):
  return "(%s, %s)"%(self.x, self.y)
 
 def get_coords(self):
  return (self.x, self.y)
 


 @staticmethod
 def from_points(P1,P2):
  return Vector2(P2[0] - P1[0], P2[1] - P1[1])

 def get_magnitude(self):
  return math.sqrt( self.x**2 + self.y**2 )
  
 def normalize(self):
  magnitude = self.get_magnitude()
  self.x /= magnitude
  self.y /= magnitude
 
 def __add__(self, vec):
  return Vector2(self.x + vec.x, self.y + vec.y)

 def __sub__(self, vec):
  return Vector2(self.x - vec.x, self.y - vec.y)


 def __neg__(self):
  return Vector2(-self.x, -self.y)

 def __mul__(self, scalar):
  return Vector2(self.x * scalar, self.y * scalar)
 
 def __div__(self, scalar):
  return Vector2(self.x / scalar, self.y / scalar)
from_points metodu verilen koordinatlara göre yeni bir Vector nesnesi döndürüyor.Burada farklı olarak @staticmethod isimli bir yapı var.Bu decorator, Java dilinde ki static niteleyicisiyle aynı işlevi görüyor.Static metodlar direkt class üzerinden erişilir.Bu metodu static yapmamızın sebebi, nesneye ait bir metod olmaması.Sadece verilen noktalara göre yeni bir Vector nesnesi oluşturması.Detaylı bilgi için:staticmethod & decorators

Vektör Uzunluğu 


get_magnitude,vektör uzunluğunu hesaplayan fonksiyonumuz.İki nokta arasındaki uzaklığı hesaplamamız gerektiğinde bunu kullanacağız.

Birim Vektör

Birim vektör, uzunluğu daima 1 br olan özel bir vektördür.Genellikle yön belirlemede kullanılır.normalize metodu vasıtasıyla bileşenleri magnitude değerine bölerek birim vektörümüzü hesaplıyoruz.

Vektörleri Toplama


 __add__ metodu, 2 vektörü toplayıp yeni bir vektör nesnesi döndürmektedir.Burada python'un bize sunduğu operator overloading özelliğini kullanıyoruz.Bu sayede add() şeklinde bir fonksiyon yaratıp AB.add(BC) şeklinde kullanmak yerine direkt AB + BC yazarak toplama işlemini gerçekleştirebileceğiz.
bknz:special method names

Bu şekilde kullanabileceğimiz diğer metodlar __sub__,__neg__,__mul__,__div__. Bunlarda sırasıyla çıkartma,negatifini alma,çarpma ve bölme.Mesela bulunduğumuz yönün tam tersine hareket etmemiz gerekiyorsa bu durumda yapacağımız vektörün negatifini almak.Bu şekilde aynı mesafede ama ters yönde hareket etmiş oluruz.

Şimdi vector sınıfımızı kullanarak bir uygulama yapalım:
 # -*- coding: utf-8 -*-

import pygame
from pygame.locals import *
from sys import exit

pygame.init()
sprite_image_filename = 'ball.png'
screen = pygame.display.set_mode((640, 480), 0, 32)

sprite = pygame.image.load(sprite_image_filename)
sprite = pygame.transform.scale(sprite,(30,30))
clock = pygame.time.Clock()
position = Vector2(100.0, 100.0)
speed = 250.
heading = Vector2()

while True:
 for event in pygame.event.get():
  if event.type == QUIT:
   exit()
  if event.type == MOUSEBUTTONDOWN:
   destination = Vector2(*event.pos) - Vector2(*sprite.get_size())/2.0
   heading = Vector2.from_points(position.get_coords(), destination.get_coords())
   heading.normalize()
 
 screen.fill((0,0,0))
 screen.blit(sprite, position.get_coords())
 time_passed = clock.tick()
 time_passed_seconds = time_passed / 1000.0
 distance_moved = time_passed_seconds * speed
 position += heading * distance_moved
 pygame.display.update()

Uygulama ekrana bir top nesnesi yerleştiriyor ve faremizin sol tuşuna tıkladığımızda nesnemiz cursor'a doğru hareket etmeye başlıyor.Tıklamamıza bağlı olarak sürekli cursor'un bulunduğu konuma yöneliyor.Şuan için event kısmını görmezden gelebilirsiniz.Kodumuzu biraz açalım.

İlk başta pozition ve heading-yön vektörümüz- yaratıyoruz.Yön vektörümüzün oluşma olayını ise farenin sol tuşuyla tetikliyoruz.
 
destination = Vector2(*event.pos) - Vector2(*sprite.get_size())/2.0
heading = Vector2.from_points(position.get_coords(), destination.get_coords())
heading.normalize()
Burada hedef vektörümüzü-destination- hesaplamak için cursor'un bulunduğu konumdan -event.pos- resmimizin merkezini hedef alan vektörü çıkartıyoruz.Bu şekilde hedef koordinatımızı belirlemiş olduk.Bu koordinatları kullanarak heading-birim vektör-oluşturuyoruz.En son normalize ettiğimizde birim vektörümüz hazır durumda.

Burada birşey dikkatinizi çekmiştir.
 
destination = Vector2(*event.pos) - Vector2(*sprite.get_size())/2.0
Buradaki  işareti girilecek parametre sayısını genişletmeye yarıyor.Mesela
 def func(*args):
dediğimizde, fonksiyona gönderilecek parametre sayısının belli olmadığını belirtiyoruz.Yani
Vector2(event.pos[0], event.pos[1])  ile Vector2(*event.pos) aynı şeyi ifade ediyor.Eğer hedef koordinatları uzun yoldan hesaplamak isteseydik:
destination_x = event.pos[0] – sprite.get_width()/2.0
destination_y = event.pos[1] – sprite.get_height()/2.0
destination = (destination_x, destination_y)
bu şekilde yazmamız gerekecekti.
Son olarak oyun döngümüzde speed * time_passed_seconds ile nesnenin ne kadar yol aldığını hesaplayıp heading vektörü ile çarpıyoruz.Son elde ettiğimiz vektör, pozisyonumuzdaki değişikliği bize veriyor.Dikkat ettiyseniz time-based hareket kullandık.Bundan sonraki bütün uygulamalarımızı bu şekilde yapacağız.
Kaynakça:

Çarpışma Kontrolü

Geldik bu bölümün son yazısına.Daha önce harekete giriş kısmında cismimizi hareket ettirip,ekranımızdan çıkmaması için koordinatları kontrol etmiştik.Aslında orada çarpışma kontrolünü yaptık.Şimdi iki cismin çarpışıp çarpışmadığını kontrol edeceğiz.Bildiğiniz gibi cisimlerimizi ekrana yerleştirirken çeşitli parametreler kullanıyorduk:top,left,bottom...Resim üzerinden konuşursak
right > x >left  ve  bottom > y >top olduğunda çarpışma gerçekleşmiş demektir.Şimdi bunu koda dökelim.
İlk aklımıza gelen,
def isPointInside(x,y,rect):
 if (x > rect.left) and (x < rect.right) and (y > rect.top) and (y < rect.bottom):
  return True
 else:
  return False
şeklinde bir fonksiyon tanımlayıp bütün parametreleri teker teker kontrol etmek.Ama buna hiç gerek yok.Bu işi bizim yerimize yapan bir fonksiyon var:colliderect().
Fonksiyonun kullanımı şu şekilde:
obj.colliderect(hedefObj)
Buradaki obj pygame.Rect sınıfından türemiş bir nesne.Aynı şekilde hedefObj de bir Rect nesnesi.Şimdi bu bilgiyi kullanarak iki cismin çarpışmasını kontrol edelim.Daha önce yazdığımız hareket koduna detectCollision metodu ekleyelim:
def detectCollision(obj1,obj2):
 
 if obj1.pos.colliderect(obj2.pos):
  if obj1.dir == Direction.UPLEFT:
   obj1.dir = Direction.DOWNRIGHT
  
  if obj2.dir == Direction.DOWNRIGHT: 
   obj2.dir = Direction.UPLEFT
  
  if obj1.dir == Direction.DOWNLEFT:
   obj1.dir = Direction.UPRIGHT
  
  if obj2.dir == Direction.UPRIGHT: 
   obj2.dir = Direction.DOWNLEFT
  
  if obj1.dir == Direction.DOWNRIGHT:
   obj1.dir = Direction.UPLEFT
  
  if obj2.dir == Direction.UPLEFT: 
   obj2.dir = Direction.DOWNRIGHT
  
  if obj1.dir == Direction.UPRIGHT:
   obj1.dir = Direction.DOWNLEFT
  
  if obj2.dir == Direction.DOWNLEFT: 
   obj2.dir = Direction.UPRIGHT
İlk if şartı çarpışma olup olmadığını kontrol ediyor.Diğer if durumları ise duruma göre yönümüzü değiştirmemizi sağlıyor.Ana döngümüz ise şu şekilde:
while True:
 ...
 detect_collision(nesne1,nesne2)
 nesne1.move(time_passed_seconds)
 nesne2.move(time_passed_seconds)
 
  
 pygame.draw.rect(window,GREEN,nesne1.pos)
 pygame.draw.rect(window,RED,nesne2.pos)
 
 pygame.display.flip()
Hareket metodunu çalıştırmadan önce çarpışma olup olmadığını kontrol ediyoruz.Kodumuzun tamamı:
#!/usr/bin/env python
import pygame
from pygame.locals import *


class Direction(object):
 DOWNLEFT = 1
 DOWNRIGHT = 3
 UPLEFT = 7
 UPRIGHT = 9
  
class Obj(object):
 """docstring for Cisim"""
 
 def __init__(self):
  self.pos = pygame.Rect((200,200), (20, 20))
  self.pixel_per_second= 100
  self.dir = Direction.UPLEFT

 def move(self,second):
  if self.dir == Direction.DOWNLEFT:
   self.pos.left -= self.pixel_per_second * second
   self.pos.top += self.pixel_per_second * second

  if self.dir == Direction.DOWNRIGHT:
   self.pos.left += self.pixel_per_second * second
   self.pos.top += self.pixel_per_second * second

  if self.dir == Direction.UPLEFT:
   self.pos.left -= self.pixel_per_second * second
   self.pos.top -= self.pixel_per_second * second

  if self.dir == Direction.UPRIGHT:
   self.pos.left += self.pixel_per_second * second
   self.pos.top -= self.pixel_per_second * second

  # check if the block has move out of the window
  if self.pos.top < 0:
   if self.dir == Direction.UPLEFT:
    self.dir = Direction.DOWNLEFT
   if self.dir == Direction.UPRIGHT:
    self.dir = Direction.DOWNRIGHT
  if self.pos.bottom > 600:
  # poslock has moved past the posottom
   if self.dir == Direction.DOWNLEFT:
    self.dir = Direction.UPLEFT
   if self.dir == Direction.DOWNRIGHT:
    self.dir = Direction.UPRIGHT
  if self.pos.left < 0:
  # poslock has moved past the left side
   if self.dir == Direction.DOWNLEFT:
    self.dir = Direction.DOWNRIGHT
   if self.dir == Direction.UPLEFT:
    self.dir = Direction.UPRIGHT
  if self.pos.right > 800:
  # poslock has moved past the right side
   if self.dir == Direction.DOWNRIGHT:
    self.dir = Direction.DOWNLEFT
   if self.dir == Direction.UPRIGHT:
    self.dir =Direction. UPLEFT


def detect_collision(obj1,obj2):
 
 if obj1.pos.colliderect(obj2.pos):
  if obj1.dir == Direction.UPLEFT:
   obj1.dir = Direction.DOWNRIGHT
  
  if obj2.dir == Direction.DOWNRIGHT: 
   obj2.dir = Direction.UPLEFT
  
  if obj1.dir == Direction.DOWNLEFT:
   obj1.dir = Direction.UPRIGHT
  
  if obj2.dir == Direction.UPRIGHT: 
   obj2.dir = Direction.DOWNLEFT
  
  if obj1.dir == Direction.DOWNRIGHT:
   obj1.dir = Direction.UPLEFT
  
  if obj2.dir == Direction.UPLEFT: 
   obj2.dir = Direction.DOWNRIGHT
  
  if obj1.dir == Direction.UPRIGHT:
   obj1.dir = Direction.DOWNLEFT
  
  if obj2.dir == Direction.DOWNLEFT: 
   obj2.dir = Direction.UPRIGHT
  
pygame.init()
WINDOWWIDTH = 800
WINDOWHEIGHT = 600
FPS = 60
WHITE = (255,255,255)
GREEN = (0,  255, 0)
RED =   (255, 0, 0)
BLUE =  (0, 0,139)
window = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
clock = pygame.time.Clock()
nesne1 = Obj()
nesne2 = Obj()

nesne2.dir = Direction.UPRIGHT
  
while True:
 window.fill(WHITE)
 time_passed = clock.tick(FPS)
 time_passed_seconds = time_passed/1000.0
 detect_collision(nesne1,nesne2)
 nesne1.move(time_passed_seconds)
 nesne2.move(time_passed_seconds)
 
  
 pygame.draw.rect(window,GREEN,nesne1.pos)
 pygame.draw.rect(window,RED,nesne2.pos)
 
 pygame.display.flip()
 

12 Haziran 2013 Çarşamba

OCCUPython Yarışması

Merhaba arkadaşlar.

Hepimiz 15 gündür ülkemizde yaşananları biliyoruz, takip ediyoruz. Bizde bu konu üzerinde ufak bir yarışma başlatalım dedik. Yarışmanın detayları aşağıdadır.

Yarışma

Gezi parkı ve diğer şehirlerde yaşanan gösterileri ele alan ve durumu ifade edebilen python uygulamaları geliştirmenizi bekliyoruz.


Kurallar

Hakaret ve ciddi anlamda provakatör hareketler yasaktır.

Yarışma İçeriği
  1. Main dosyanın adı occu.py adında olmalıdır.
  2. Bir adet açıklama kısmı olmalıdır. Açıklama kısmında nickname veya isim belirtiniz. Bu paragraf kodlar yayınlanırken bizim tarafımızdan kullanılacaktır.
  3. Uygulama herhangi bir şey yapıyor olmasına gerek yoktur. Düşüncelerinizi sadece print ile ekrana basan uygulamalarda kabul görmektedir.
Örnekler
1 - Gezi parkında başlayan ve twitter üzerinde occupy hashtag'i ile başlayan oluşuma destek veren illeri listeleyen bir program olabilir.
2 - Gezi parkı ihtiyaç listesini tweet atan bir python programı olabilir.
3 - Provakatörler fotoğraflarını belirtilen bir mail adresine mail atan python programı olabilir.

Örnekleri çoğaltabilirsiniz. Tüm yaratıcılığınızı konuşturmanızı beklemekteyiz. 

Başvurular:
mehmet@mehmetince.net adresine [occu.py] başlığı ile gönderiniz.