Bir zamanlar ortalığı kasıp kavuran Angry Birds oyununun çok kısa bir sürede nasıl yapıldığını anlatmaya çalışacağım. Bu yazıyı uygulayanlar yazının sonunda iOS telefonlar için bir oyun ortaya çıkarmış olacaklar.
Bu yazıdaki uygulamayı ortaya çıkarmak için aşağıdaki gereksinimleri karşılamanız gerekmektedir.
- Bir MacOs işletim sistemi (En güncel versiyonu olmalı, şu an en güncel versiyon: 10.15.4 Catalina)
- Xcode IDE
- Apple üyeliği (Developer hesabı değil, sadece ücretsiz apple üyeliği)
Angry Birds Oyunu

Xcode uygulamasını açınca karşımıza Şekil-1’deki karşılama ekranı çıkacaktır. Buradan 2. seçenek olan “Create a new Xcode project” seçeceğini seçiyoruz.

Yeni proje oluştur kısmını geçtikten sonra bizi “Choose a template for your new project:” ekranı karşılacaktır. Buradan Game seçeceğini seçip devam ediyoruz.

Yeni projemizin oyun temasında olacağını seçip ilerledikten sonra karşımıza Şekil-3’deki proje yapılandırma ekranı gelecektir. Android üzerinden uygulama ve oyun geliştiren arkadaşların aşina olabileceklerini düşündüğüm bu ekranda projemizi isimlendirip uygulama geliştirici bilgilerini giriyoruz.
Yeni oyun projesi oluşturup ilerlediğiniz zaman Şekil-3’de görüldüğü gibi yazılım dili Swift ve Oyun Teknolojisi SpriteKit seçili olarak gelmektedir.
Ekranda bize seçilebilir olarak sunulan diller:
- Swift
- Objective-C
Bunun yanında oyun teknolojileri olarak da bize sunulan teknolojiler:
- RealityKit
- SceneKit
- SpriteKit
- Metal
Yukarıdaki seçeneklerden bu oyun için gerekli olanlar seçili olarak gelenlerdir. Ancak 3 boyutlu oyunlar veya tüm yapay etkileşimleri kendinizin yazacağı bir oyun oluşturmak istersek oyun motorlarından SceneKit veya Metal opsiyonlarını seçebilirdik. Şu an için bunlar bizim için önemli değiller.

Proje yapılandırmamızı tamamladıktan sonra Xcode Şekil-4’deki gibi oyun teması ile açılacaktır. Proje Navigatöründe normal bir iOS uygulaması geliştirilen gördüğümüz ögelerden farklı ögeler görülmektedir. Bunlardan bizim için önemli olarak:
- GameScene.sks
- GameScene.swift
- GameViewController.swift
Ortaya çıkarmaya çalışacağımız Angry Birds 2D oyunu için bu dosyaları kullanacağız.

Oyunun görsel kısmının toolbox kullanarak çek bırak yöntemiyle oluşturulduğu dosya Şekil-5’de de görüldüğü gibi GameScene.sks dosyasıdır. Bu uygulamayıda bu şekilde .ek bırak yöntemiyle oluşturacağız. Ancak bir oyun oluşturulurken bu şekilde sabit girdiler ile değil dinamik ve her cihada uyumlu şekilde kod ile oluşturulurlar. Biz bu uygulamayı çek bırak yöntemiyle oluşturacağız ancak isteyenler yazının sonunda bunu çok basit bir şekilde kod ile de oluşturabilecek seviyeye gelecektir.

Dikkat edilirse proje başlarken uygulama ekranımız dik bir şekilde görüntülenmektedir. Bizim ortaya çıkaracağımız oyun ise yatay oynanan bir oyun olacaktır. Bu sebeple bazı ayarlamalar yapmak gerekmektedir. İlk olarak GameScene.sks içersinde sağ kısımda Şekil-6’da görülen Attributes Inspector kısmında boyutları yatay olarak ayarlanmalıdır. Bunu yaparken size kısmına gelip telefon modeli seçip ölçüleri girebiliriz.
Uygulamamız için iPhone 6s Plus yatay(LandScape) ölçülerini kullacağız.
Ölçüleri yatay olarak girmemiz yeterli olmayacaktır. Sadece yatay görüntü almak için Proje Navigatörü kısmından Info.plist dosyasını açıp Şekil-7’de de görünen Supported Interface Orientations ve Supported Interface Orientations(iPad) kısımlarındaki Portrait seçeneklerini siliyoruz.

Ekranın yatay ayarlarını yaptıktan sonra GameScene.sks son hali Şekil-8’de görüldüğü gibi yatay bir şekil ayalacaktır.

Xcode üzerinden yeni oyun projesi açıldığı zaman bir başlangıç tasarımı ve kod bloğu vermektedir. Tasarım kısmı Şekil-8’de görüldüğü gibi “Hello, World” yazısı yer alan bir ekrandır. Kod kısmi ise Şekil-9’da görüldüğü gibidir.

Yeni projede otomatik olarak gelen bu kodlar ekranda bir takım aksiyonlar oluşturmaktadır. Ancak bu kodlar bizim işimize yaramadığı için fonksiyonların hepsini siliyoruz.
Projemizde bizim işimize yarayacak fonksiyonlar UIKit tarafından sağlanmaktadır. Örnek verecek olursak ekrana dokunma, dokunmayı sürdürme, dokunmayı bırakma gibi. Bu sebeple otomatik olarak oluşturalan kodları silerken bu fonksiyonları silmek yerine içlerini temizleyebilirsiniz.

Otomatik oluşturalan kodların temizlenmesinden sonra Şekil-10’daki gibi temiz bir GameScene.swift dosyası elde etmiş olacağız.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
}
}
Temizlenmiş GameScene.swift dosyası kodları yukarıda da verildiği gibi olacaktır. Bu kısımdan sonra tasarım ve kodlama olarak ihtiyaçlarımıza göre eklemeler yapacağız.
AngryBirds Oyununun Görsel İhtiyaçları
Oyunda kullacağımız bazı görsel ögelere ihtiyacımız var. Bunlar:
- Oyun arkaplanı
- Kuş
- Kutular
- Sapan veya Ağaç
Bu ögelere internetten kolayca erişebiliriz. Google Görsel aramalardan PNG formatında ve ücretsiz kullanım izni olan görselleri kullanabilir veya bu iş için oluşturulmuş web sitelerinden aramalar yapabiliriz.
Kullacağımız Görseller:
Arkaplan Kutu Kuş Ağaç
Bu görsel ögeleri projemize eklemek için sol kısımda yer alan Proje Navigatörü kısmından Assets.xcassets içerisine girip Şekil-11’deki gibi ögeleri finder üzerinden çek bırak ile bu kısma taşıyoruz. Burada ögelerin ismi çok önemlidir çünkü ögeleri isimlerine göre çağıracağız.

Ögeleri ekledikten sonra sırayla bu ögeleri projemizin görsel kısmına ekleyelim. Bunun için GameScene.sks içerisine gelip, sol üst kısımdaki Library (+) alanından Color Sprite objesi seçiyoruz. Şekil-12’de ilgili pencereyi görebilirsiniz.

GameScene üzerinden eklediğimiz Color Sprite Şekil-13’de görüldüğü gibi eklenmektedir.

Color Sprite ekledikten sonra bunu arkaplan resmimize çevirip ekran boyutlarına getirmeliyiz. Arkaplan resmini seçmek için sağ kısımdaki Sprite özelliklerinden Texture kısmından arkaplan ögemizi seçebiliriz. Daha önce Şekil-11’deki gibi eklemiş olduğumuz görsel ögelerimiz bu kısımda seçilebilir durumda.

Arkaplanı ekledikten sonra Şekil-14’deki son hali elde etmiş olacağız. Bu kısımdan sonra ağaç, kuş ve kutu ögelerini de yine aynı şekilde Color Sprite ögesi kullanarak ekranda istediğimiz yere konumlandıracağız.

Oyun tasarım kısmını bitirdik, burada son olarak bilmemiz gereken eklediğimiz Color Sprite objelerinin Sprite özelliklerinde name kısmında isimlendirmeyi unutmamak.

Tasarımı yaptıktan sonra bir test yaptık ve eklediğimiz kuş ekranda görünmemekte.

Bunun sebebini web tasarım veya JS tabanlı oyun ile ilgilenen arkadaşlar tahmin etmiştir ki bunun sebebi kat sıralaması yani z-index’tir.
Kuş arkaplan ögesinden geride kaldığı için görünmemektedir. Bunu çözmek için arkaplan Color Sprite seçip z değeri -1 girilebilir.
Çözüm için yapılaması gereken Şekil-17’de gösterilmiştir.
Görüntüleme aşaması da geçildikten sonra sıra kodlama aşamasındadır.
var zemin = SKSpriteNode()
var kus = SKSpriteNode()
var kutu1 = SKSpriteNode()
var kutu2 = SKSpriteNode()
var kutu3 = SKSpriteNode()
var kutu4 = SKSpriteNode()
var kutu5 = SKSpriteNode()
var kutu6 = SKSpriteNode()
var kutu7 = SKSpriteNode()
var kutu8 = SKSpriteNode()
var atisDurumu = false
Kuş ve kutuların tanımını yaptıktan sonra oluşmak için;
override func didMove(to view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
self.scene?.scaleMode = .aspectFit
self.physicsWorld.contactDelegate = self
zemin = childNode(withName: "zemin") as! SKSpriteNode
zemin.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: zemin.size.width, height: zemin.size.height))
zemin.physicsBody?.affectedByGravity = true
zemin.physicsBody?.isDynamic = true
zemin.physicsBody?.mass = 100
kus = childNode(withName: "kus") as! SKSpriteNode
kus.physicsBody = SKPhysicsBody(circleOfRadius: 22.5)
kus.physicsBody?.affectedByGravity = false
kus.physicsBody?.isDynamic = true
kus.physicsBody?.mass = 0.15
kus.physicsBody?.allowsRotation = false
let size = CGSize(width: 45, height: 45)
kutu1 = childNode(withName: "kutu1") as! SKSpriteNode
kutu1.physicsBody = SKPhysicsBody(rectangleOf: size)
kutu1.physicsBody?.isDynamic = true
kutu1.physicsBody?.affectedByGravity = true
kutu1.physicsBody?.allowsRotation = true
kutu1.physicsBody?.mass=0.1
kutu2 = childNode(withName: "kutu2") as! SKSpriteNode
kutu2.physicsBody = SKPhysicsBody(rectangleOf: size)
kutu2.physicsBody?.isDynamic = true
kutu2.physicsBody?.affectedByGravity = true
kutu2.physicsBody?.allowsRotation = true
kutu2.physicsBody?.mass=0.1
kutu3 = childNode(withName: "kutu3") as! SKSpriteNode
kutu3.physicsBody = SKPhysicsBody(rectangleOf: size)
kutu3.physicsBody?.isDynamic = true
kutu3.physicsBody?.affectedByGravity = true
kutu3.physicsBody?.allowsRotation = true
kutu3.physicsBody?.mass=0.1
kutu4 = childNode(withName: "kutu4") as! SKSpriteNode
kutu4.physicsBody = SKPhysicsBody(rectangleOf: size)
kutu4.physicsBody?.isDynamic = true
kutu4.physicsBody?.affectedByGravity = true
kutu4.physicsBody?.allowsRotation = true
kutu4.physicsBody?.mass=0.1
kutu5 = childNode(withName: "kutu5") as! SKSpriteNode
kutu5.physicsBody = SKPhysicsBody(rectangleOf: size)
kutu5.physicsBody?.isDynamic = true
kutu5.physicsBody?.affectedByGravity = true
kutu5.physicsBody?.allowsRotation = true
kutu5.physicsBody?.mass=0.1
kutu6 = childNode(withName: "kutu6") as! SKSpriteNode
kutu6.physicsBody = SKPhysicsBody(rectangleOf: size)
kutu6.physicsBody?.isDynamic = true
kutu6.physicsBody?.affectedByGravity = true
kutu6.physicsBody?.allowsRotation = true
kutu6.physicsBody?.mass=0.1
kutu7 = childNode(withName: "kutu7") as! SKSpriteNode
kutu7.physicsBody = SKPhysicsBody(rectangleOf: size)
kutu7.physicsBody?.isDynamic = true
kutu7.physicsBody?.affectedByGravity = true
kutu7.physicsBody?.allowsRotation = true
kutu7.physicsBody?.mass=0.1
kutu8 = childNode(withName: "kutu8") as! SKSpriteNode
kutu8.physicsBody = SKPhysicsBody(rectangleOf: size)
kutu8.physicsBody?.isDynamic = true
kutu8.physicsBody?.affectedByGravity = true
kutu8.physicsBody?.allowsRotation = true
kutu8.physicsBody?.mass=0.1
}
Kodlar ile kuş ve kutuyu oluşturduk. Yani görsel olarak çek bırak ile oluşturduğumuz ögeleri kod ile tanımladık.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
kus.physicsBody?.affectedByGravity = true;
atisDurumu = true
if let touch = touches.first {
let touchLocation = touch.location(in: self)
let touchNodes = nodes(at: touchLocation)
if touchNodes.isEmpty == false{
for node in touchNodes{
if let sprite = node as? SKSpriteNode{
if sprite == kus{
kus.position = touchLocation
}
}
}
}
}
}
Ekrana dokunma başlayınca olan kodlarımız yukarıdaki şekilde,
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let touchLocation = touch.location(in: self)
let touchNodes = nodes(at: touchLocation)
if touchNodes.isEmpty == false{
for node in touchNodes{
if let sprite = node as? SKSpriteNode{
if sprite == kus{
kus.position = touchLocation
}
}
}
}
}
}
Dokunma ekran üzerinde devam ettikçe çalışacak kod yukarıdaki şekilde,
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if atisDurumu == false{
if let touch = touches.first {
let touchLocation = touch.location(in: self)
let touchNodes = nodes(at: touchLocation)
if touchNodes.isEmpty == false{
for node in touchNodes{
if let sprite = node as? SKSpriteNode{
if sprite == kus{
let DX = -(touchLocation.x - -272)
let DY = -(touchLocation.y - -10)
let impulse = CGVector(dx:DX*1.4, dy:DY*1.4)
kus.physicsBody?.applyImpulse(impulse)
kus.physicsBody?.affectedByGravity = true
atisDurumu = true
}
}
}
}
}
}
}
Ekrandan elimizi çektiğimizde çalışacak kodlar yukarıdaki şekilde,
override func update(_ currentTime: TimeInterval) {
if let birdPhysicsBody = kus.physicsBody {
if birdPhysicsBody.velocity.dx <= 0.1
&& birdPhysicsBody.velocity.dy <= 0.1
&& birdPhysicsBody.angularVelocity <= 0.1
&& atisDurumu == true{
kus.physicsBody?.affectedByGravity = false
kus.physicsBody?.velocity = CGVector(dx:0, dy:0)
kus.physicsBody?.angularVelocity = 0
kus.position = CGPoint(x: -272, y: -10)
atisDurumu = false
}
}
}
İşlem süresince bu kodlar çalışmaktadır, istediğimiz durumlar oluşunca istediğimiz işlemi yaptırabiliriz.
Kolay Gelsin 🙂
Bir yanıt yazın