11 Ekim 2009 Pazar

Adobe Director ile Basit Bir Oyun

Bu eğitselde, basit olarak, adobe director kullanarak nasıl bir oyun yapılacağını göreceksiniz. İnternet üzerinden satılan ve oldukça ünlü olan "MarbleBall"a benzer bir oyun oluşturacağız. Kısaca gerekli adımları sıralamak gerekirse;
1 - Oyun için modellerin hazırlanması ve uygun bir formatta export edilmesi.
2 - Menu ve bölümlerin bitiş ekranları için gerekli flash dosyalarının hazırlanması.
3 - Hazırlanan dosyaların director'a import edilmesi ve ardından küçük bir script yazılmasıyla oyunun tamamlanması.

Birinci Bölüm: 3dsMax ile modellerin hazırlanması

Yapıcağımız oyun sadece öğrenim amaçlı olduğundan grafiklere fazla önem vermiyoruz. Topun ilerleyebileceği bir sahne hazırlıyoruz (Aşağıdaki gibi)

director_2

Burada dikkat edilmesi gereken, isimlendirmedir. Sahnenizi tamamladıktan sonra, oluşturduğunuz topa 'ball' ismini verin. Geriye kalan, topun üzerinde gezmesi gereken geometrilerin hepsini seçin ve onları gruplandırın.

director_3

Bu grubu 'levelgeo' olarak adlandırın.

Oyunumuzda bir seviyenin tamamlanması için, topun bir yere ulaşması gerekiyor. Biz de bu yeri bir kutu ile belirtiyoruz.

director_4

Bu kutunun ismine 'levelfin' adını veriyoruz.

Artık oyun için bir sahnemiz var. Şimdi bunu director'a import edebilmek için '.w3d' formatında export ediyoruz. Dosyaya 'level1.w3d' adını veriyoruz. Küçük bir hatırlatma, bu eğitselin sonunda, oluşturduğumuz tüm dosyaları bulabilirsiniz.

İkinci Bölüm: Flash ile gerekli menü ve ekranların hazırlanması

Kullanıcının rahatça oynunuzu oynaması için oyunun düzgün bir menüsü olmalı. Menüyü director'da da hazırlayabilirsiniz fakat bu işlem flash'a göre daha uzun ve daha zordur. Arayüz oluştururken flash'ı kullanmak daha akıllıca olacaktır.

Yeni bir flash dosyası (ben A.S 2 kullandım) oluşturun ve 'menu.fla' olarak kaydedin. Menünüzün sığabileceği bir çözünürlük seçin, ben 640x480 seçtim. Bundan sonra düzgün bir menü oluşturun. Bu eğitselde az da olsa flash bildiğinizi varsayıyorum. Ben aşağıdaki gibi bir menü oluşturdum.

director_5

Burada rastgele dikdörtgenler çizdim, özel birşey yok. Sadece 'yüksek puanlar' ve 'kim yaptı bunu' butonları için iki tane movieclip oluşturdum (resimde sağda). Bu butonlara tıklanınca bu movieclip'ler menünün önüne geliyor. 'Tamam'a tıklanınca da geri gidiyor. Bunun için ufak bir script yazdım. İlk frame'e bu moviecliplerin gitmesi için aşağıdaki scripti ekledim.

{code type=php}
_root.creditsbox._x = 10000;
_root.scorebox._x = 10000;
{/code}

Yine bu scripti her movieclip'teki 'Tamam' butonlarına ekledim. Menüdeki tuşlara tıklandığında da bu moviecliplerin ekrana gelmesi içinde aşağıdaki scripti ekledim.
İlki 'Kim Yaptı Bunu' butonu için, ikinciside 'Yüksek Puanlar' için, zaten script'tende belli oluyor.

{code type=php}
on(press)
{
_root.creditsbox._x = 107.6;
_root.creditsbox._y = 147.1;
}

on(press)
{
_root.scorebox._x = 107.6;
_root.scorebox._y = 112.1;
}
{/code}

Son olarak da 'Yeni Oyun' butonuna basıldığında bizim director'a haber vermemiz gerekiyor. Bunun için 'Yeni Oyun' butonuna aşağıdaki scripti ekliyoruz.

{code type=php}
on(press)
{
getURL("event:triggerNewGame");
}
{/code}

Bu ne demek oluyor hemen açıklıyorum. Director içinde 'triggerNewGame' diye bir fonksiyon oluşturursanız, bu butona basıldığında otomatik olarak director'da o fonksiyon çağırılacaktır.

İkinci olarak level tamamlandığında da kullanıcıyı bir ekranla karşılamalıyız. Bunun için 'fin.fla' adında bir flash dosyası oluşturuyoruz. Yine bir arayüz oluşturup kullanıcının bu ekranı geçmesi için bir buton koyuyoruz. Tekrar director'a mesaj gönderebilmesi için ona da küçük bir script ekliyoruz.

{code type=php}
on(press)
{
getURL("event:triggerOk");
}
{/code}

director_6

Bu durumda menümüzde hazırlanmış oldu. Şimdi ise son bölüme geçiyoruz. Burada oluşturduğumuz herşeyi birleştirip bir oyun haline getireceğiz.

Üçüncü Bölüm: Bir araya getiriyoruz

Adobe Director'u başlatın. Yeni bir dosya oluşturun ve 'clumsyball' olarak kaydedin. Bu aşamadan sonra isterseniz sürükle bırakla, isterseniz import seçeneğiyle oluşturduğumuz dosyaları director'un içine atın.

director_7

Stage'e import ettiğimiz 'menu'yü ekliyoruz. Ekledikten hemen sonra 'Property Inspector'den hem 'start frame'ini hem de 'end frame'ini 1 yapıyoruz.

director_8

Daha sonra flash sekmesinde, scale seçeneğini 'no scale' yapıyoruz.

director_9

Daha sonra stage'e 'level1'i ekliyoruz. Ekledikten sonra yine property inspector'den boyutunu 640x480 olarak ayarlıyoruz, bu sefer start ve end frame'ini 2 yapıyoruz.

director_10

Son olarak da 'fin'i ekliyoruz. Onunda start ve end frame'ini 3 yapıyoruz. Flash sekmesinden de 'no scale' seçeneğini seçmeyi unutmayın.

Gerekli tüm ayarlamaları yaptık, şimdi ise script yazmaya başlayabiliriz. Stage'e eklemeden önce scriptlerimizi oluşturuyoruz. Sağ altta bulunan 'Behavior Inspector'den 'New Behavior' seçeneğini seçiyoruz.

director_11

Director'da scriptler 'Behavior' olarak adlandırılmıştır. İlk behavior'umuza 'menu_behavior' adını veriyoruz. Bu kodu menüye ekleyeceğiz. Burada daha önce flash'ı oluştururken belirttiğimiz 'triggerNewGame' fonksiyonunu tanımlıyoruz. Bu fonksiyonda çok önemli bir kod yok, sadece 2.frame'e gitmesini söylüyoruz. on exitFrame'de ise, bu frameden ayrılmamasını sağlıyoruz aksi taktirde menüde işlem yapamadan bir sonraki frame'e geçer.

{code type=css}
on triggerNewGame
beep
go to frame 2
end

on exitFrame
go to frame 1
end
{/code}

İkinci olarak, oyun tamamlandığındaki frame için bir behavior ekliyoruz. Bunun da adı 'fin_behavior' olsun. Yine daha önce flash'da belirttiğimiz 'triggerOk' fonksiyonunu tanımlıyoruz. 'exitFrame'de bu frame'den ayrılmaması için bir kod yazıyoruz. Bir öncekiyle aynı işi yapıyor ancak farklı şekilde, sadece gösterim amaçlı.

{code type=css}
on exitFrame
_movie.go(_movie.frame)
end

on triggerOk
beep
go to frame 1
end
{/code}

Şimdi ise en önemli kısıma geldik. 2. frame için kod yazmaya başlıyoruz. Ama bu behavior'u eklemeden önce cast'e bir media element eklemeliyiz. Bunun için insert->media element->Physics'i seçiyoruz. Oyunumuzdaki tüm çarpışma kontrolünü ve simülasyonu bu obje(PhysX) yapıcak.

director_12

Son olarak, oyundaki işlemlerin yapılması için bir behavior daha ekliyoruz. Eklediğimiz bu behavior'a 'game_behavior' ismini verip aşağıdaki kod'u ekliyoruz. Tüm açıklamalar kodun üzerinde yapılmıştır.

{code type=css}
-- Basit bir marbleball oyunu
-- bu property'ler ok tuşlarını tutmak için kullanılıyor
property pLeftArrow, pRightArrow, pDownArrow, pUpArrow

-- level'ımızı tutan property
property p3Dmember
-- ball geometrimize erişmemize yarayan değişken
property pBall

-- sahne kamerası
property pCam
-- level geometrimizi tutan property
property terrain
-- levelfin kutusu için property
property pFin

-- Physx ile ilgili olan değişkenler
-- fizik objemizi tutuan değişken
property pDirPhyz
-- adım zamanı ve alt adımlar
property pTimeStep,pSubSteps
-- levelgeo için rigid body
property rterrain
-- ball için rigid body
property rBall
-- fin kutusu için rigid body
property rFin

-- kameranın yeni pozisyonunu hesaplarken kullandığımız
-- geçici bir değişken
property t
-- kameranın ile top arasındaki mesafenin vektörü
property distvec
-- top'un ilk pozisyonu
property first_pos


-- bu ilk kez çağırılacak olan fonksiyondur.
on beginSprite me
-- her seferinde member'a erişmek zorunda kalmamak için
-- bir değişkene atıyoruz.
p3Dmember = member("level1")
p3Dmember.resetWorld()

--PhysX içinde aynısını yapıyoruz, ve istediğimiz değişkenleri
-- belirtiyoruz.
pDirPhyz = member("phsyX")
pTimeStep = 0.025
pSubSteps = 5
-- Phsyx ile level'ımızı bağlıyoruz.
pDirPhyz.Init(p3Dmember, vector(1,1,1),#equal, pTimeStep, pSubSteps )

-- çeşitli parametreleri physx'e veriyoruz.
-- ağırlık, kontak toleransı, sürtünme katsayısı,
-- lineer ve açısal sönümleme ve iade (bir yüzeye çarpması durumunda
-- eski enerjisinin ne kadarına sahip olacağı)
pDirPhyz.gravity = vector(0,0,-80)
pDirPhyz.contacttolerance = 0.2
pDirPhyz.friction = 1
pDirPhyz.lineardamping = 1
pDirPhyz.angulardamping = 1
pDirPhyz.restitution = 0.1

-- top'umuzu bir değişkene atıyoruz.
pBall = p3Dmember.model("Ball")

-- sahnemizdeki kamerayı bir değişkene atıyoruz.
pCam = p3Dmember.camera[1]
-- uzaklık vektörümüzü hesaplıyoruz.
-- kameranın konum vektörü - topun konum vektörü
distvec = pCam.transform.position - pBall.transform.position
-- topun ilk konumunu kaydediyoruz, eğer kişi, topu aşağıya düşürürse
-- top buraya konumlandırılacak.
first_pos = pBall.transform.position

-- aslında terrain ile çok alakası olmasada böyle bir isim
-- uygun görmüşüm :)
-- level geometrimiz için bir değişken oluşturuyoruz.
terrain = p3Dmember.model("levelgeo")

-- physx de olan değişikliklerin 3d dünyamıza yansıması için
-- ikisinede modifier ekliyoruz.!!!
pBall.addmodifier(#meshdeform)
terrain.addmodifier(#meshdeform)

-- levelgeo için bir rigid body oluşturuyoruz. Zaman zaman
-- konkav olabileceği için sorun çıkmaması için bu şekli konkav
-- olarak tanımlıyoruz. ayrıca sabit bir geometri olduğundan
-- statik olarak tanımlıyoruz.
rterrain = pDirPhyz.createRigidBody(terrain.name,terrain.name,\
#concaveshape,#static)

-- Top için bir rigid body oluşturuyoruz. Bu sefer top bir küre
-- olduğundan ötürü konveks bir şekle sahip olacaktır. Ayrıca
-- topu hareket ettirmek istediğimizden onu dinamik olarak
-- tanımlıyoruz.
rBall = pDirPhyz.createRigidBody(pBall.name,pBall.name,\
#convexshape,#dynamic)
-- kütlesini 100 olarak belirliyoruz.
rBall.mass = 100

-- levelfin geometrisini bir değişkene atıyoruz.
pFin = p3Dmember.model("levelfin")
-- bunada bir modifier ekliyoruz.
pFin.addmodifier(#meshdeform)
-- bu değişkenden de bir rigid body oluşturuyoruz (konveks ve statik)
rFin = pDirPhyz.createRigidBody(pFin.name,pFin.name,#convexshape,#static)

-- topun levelfin geometrisine çarpması halinde level'ın tamamlanması lazım
-- bu yüzden bir çarpışma callback'i ekliyoruz.
-- collisionCallback fonksiyonunu daha sonra tanımlıyoruz.
pDirPhyz.registercollisioncallback(#collisionCallback, me)
-- ve son olarak callback'leri aktif hale getiriyoruz.
pDirPhyz.enablecollisioncallback()
-- böylelikle 3d ve fizik dünyamını kullanıma hazırlamış olduk.
end

-- klavyede bir tuşa basıldığında bu fonksiyon çağırılacaktır.
-- bizde bu sayede ok tuşlarımızın durumlarını güncelliyoruz.
on keyDown
if keypressed(123) then pLeftArrow = TRUE
if keypressed(124) then pRightArrow = TRUE
if keypressed(125) then pDownArrow = TRUE
if keypressed(126) then pUpArrow = TRUE
end

-- aynı şekilde herhangi bir tuş bırakılırsa da bu fonksiyon
-- çağırılacaktır.
on keyUp
if keypressed(123)<>TRUE then pLeftArrow = FALSE
if keypressed(124)<>TRUE then pRightArrow = FALSE
if keypressed(125)<>TRUE then pDownArrow = FALSE
if keypressed(126)<>TRUE then pUpArrow = FALSE
end

-- döngümüzü bu fonksiyonla belirliyoruz. Frameden her çıkmaya çalıştığında
-- gerekli işlemlerimi yaptırıyoruz ve aynı frame'e geri döndürüyoruz.
on exitFrame
-- topu hareket ettirdiğimiz fonksiyonu çağırıyoruz.
ballMove
-- kameranın yeni pozisyonunu hesaplıyoruz.
t = pCam.getWorldTransform()
-- kameranın hareket eden topa göre yeni pozisyonunu hesaplıyoruz.
t.position = distvec + pBall.transform.position
-- yumuşak bir geçiş için bu yeni pozisyon ile
-- eski pozisyonu arasında interpolayson yapmasını istiyoruz.
pCam.transform.interpolateTo(t, 100)
-- physx'e bir adım daha hesaplamasını söylüyoruz.
pDirPhyz.simulate()
-- tekrar bulunduğumuz frame'e dönmesini belirtiyoruz.
_movie.go(_movie.frame)
end

on ballMove
-- basılan tuşlara göre topun rigid body'sine kuvvet uygula
if(_key.keyPressed("s")) then
rBall.applyLinearImpulse(vector(0,-400,0))
end if
if (_key.keyPressed("w")) then
rBall.applyLinearImpulse(vector(0,400,0))
end if
if (_key.keyPressed("a")) then
rBall.applyLinearImpulse(vector(-400,0,0))
end if
if (_key.keyPressed("d")) then
rBall.applyLinearImpulse(vector(400,0,0))
end if
if (_key.keyPressed(" ")) then
--rBall.applyLinearImpulse(vector(0,0,300))
end if

-- eğer top aşağıda düştüyse, ilk pozisyonuna
-- götürmeye çalış
if pBall.transform.position.z < -30 then
rBall.attemptMoveTo(first_pos)
end if
end

-- Daha önce belirttiğimiz collison callback
-- fonksiyonunun tanımı
on collisionCallback me,colReport
-- çarpışan her cisim için kontrol yap.
repeat with k = 1 to colReport.count
rb1 = colReport[k].ObjectA
rb2 = colReport[k].ObjectB
-- eğer çarpışan objelerin biri top, diğeri levelfin ise
-- level tamamlanmıştır.
if ((rb1.name contains "ball") and ( rb2.name contains "levelfin")) then
-- oyun bitince uyarı ver ve bir sonraki frame'e geç
beep
go to frame 3
end if
end repeat
end

-- son olarak temizlik yap.
on endSprite me
if pDirPhyz.isInitialized = 1 then
pDirPhyz.destroy()
end if
end
{/code}

Dosyamızı kaydediyoruz. Normalde bu tip oyunlarda topa kuvvet yerine tork uygulanır ancak tork uygulamak için biraz daha işlem yapmamız gerekiyor. Bu yüzden örneği basit tutmak amacıyla topa kuvvet uyguluyoruz. Şimdi ise behavior'ları stage'e ekliyoruz. Bunun için ilgili frame'e gelip, behavior'u stage'e sürüklüyoruz. Son durumda 'score' aşağıdaki gibi görünmeli.

director_13

Oyunumuz neredeyse hazır. Bitirmeden önce file->publish settings'e giriyoruz. Resimdeki gibi ayarlamaları yapıyoruz. Oyunumuzu internette yayınlamak için html ve dcr formatında oluşturulmasını istiyoruz ve tam ekran olması için de html sekmesinde boyutunu %100 yapıyoruz.

director_14

director_15

Ayarları kaydettikten sonra da file menüsünden publish diyoruz. Artık oyunumuzu tamamlamış olduk. Oyunun dosyalarını aşağıdaki linkten indirebilirsiniz.

rarclumsyball.rar (166 kb)
dcrOyunu oynamak için tıklayın

2 yorum:

  1. ozan TBAKOĞLU28 Nisan 2010 03:08

    Acaba .max (max 2010 veya 2009 da olur) formatlı dosya .w3d dosyasına export etmek için program varmı sizde nette bulamadım da linkini vermemişsinizde yazının altında:D

    YanıtlaSil
  2. ozan TBAKOĞLU28 Nisan 2010 03:17

    buldum galiba export tan pling filan var sandım da not edersen altına sevinirim :D

    YanıtlaSil