Prototype ve Decorator Design Patternleri .Net Core Bir Oyunda Kullanma
Selamlar,
Bu makalede, benim oyun yazarken çok işime yarıyan 2 design pattern üzerinde konuşacağız. Prototype ve Decoratorler.
Oyunda kullanım amacı uçak, tank, helikopter gibi araçların random sayıda yaratılması ve kendilerine özgü özelliklerin atanmasıdır. Siz bu sınıflar yerine, kendi bussines objelerinizi aynı mantıkta yaratıp, istenen özellikleri ekleyebilirsiniz.
Vehicle: Öncelikle ortak olarak kullanılan araç yani “Vehicle” abstract sınıfımızı tanımlayalım. Kısaca bu sınıf bizim araçların ortak özelliklerini barındıran bir yapı. İlerde bu araca çeşitli özellikler ekliyeceğiz. Yani dekore edeceğiz :)
1 2 3 4 5 |
export abstract class Vehicle { public abstract Fire(); public abstract MoveForward() } |
WarMachine : Esas yeni özellikler eklenecek en basit aracımız WarMachine.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
export class WarMachine extends Vehicle { protected _width: number; protected _height: number; protected _left: number; constructor(left: number) { super(); this._left = left; } public Fire() { } public MoveForward() { } } |
VehicleDecorator : Vehicle’ı bünyesinde bulunduran ve ana özelliklerin implemente edildiği ara abstract sınıftır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
abstract class VehicleDecorator extends Vehicle { protected _vehicle: Vehicle; constructor(vehicle: Vehicle) { super(); this._vehicle = vehicle; } public Fire() { if (this._vehicle != null) this._vehicle.Fire(); } public MoveForward() { if (this._vehicle != null) { this._vehicle.MoveForward(); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
export class WarMachineDecorator extends VehicleDecorator { constructor(vehicle: Vehicle) { super(vehicle); } public Fire() { if (this._vehicle != null) { this._vehicle.Fire(); } } public MoveForward() { if (this._vehicle != null) { this._vehicle.MoveForward(); } } public Escape() { for (var i = 0; i < 3; i++) { this.MoveForward(); } } public Stop() { } } |
Plane: Oyunda kullanılan birçok özelliğin eklendiği en önemli ve kapsamlı araçtır. Bütün özelliklerini “WarMachineDecorator“‘dan almıştır. “WarMachineDecorator” da, “WarMachine” sınıfından dekore edilerek elde edilmiştir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
export class Plan extends WarMachineDecorator { protected _speed: number; protected _top: number; protected _left: number; constructor(vehicle: Vehicle, top: number, speed: number) { super(vehicle); this._top = top; this._top = speed; } public Fire() { if (this._vehicle != null) { this._vehicle.Fire(); } } public MoveForward() { if (this._vehicle != null) { this._vehicle.MoveForward(); } } public MoveBack() { } public MoveUp() { } public MoveDown() { } } |
Helicopter: Bu da yine size örnek teşkil etmesi amacı ile yazılan özellikleri “WarMachineDecorator“‘den genişletilmiş bir başka Helicopter sınıfıdır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
export class Helicopter extends WarMachineDecorator { protected _top: number; constructor(vehicle: Vehicle, top: number, speed: number) { super(vehicle); this._top = top; } public Fire() { if (this._vehicle != null) { this._vehicle.Fire(); } } public MoveForward() { if (this._vehicle != null) { this._vehicle.MoveForward(); } } } |
Decorator:
Şimdi farkettiyseniz amaç önce base bir sınıf yaratmak ve sonra da diğer sınıfları bu sınıftan inherit edip, özelliklerini geliştirmek yani Decore etmektir. Visual Studio IDE’si de bu design pattern’i bolca kullanmaktadır. Aşağıdaki diyagramda, LCD’den türemiş bir pencere ve pencereden dekore edilerek eklenen özellikler görülmektedir. Ben de bu oyunda basitçe yarattığım savaş araçlarının özelliklerini, en basitten karmaşığa doğru özelliklerini bir birine aktarıp, hem kod kalabalığından kurtuldum, hem de istediğim bir özellikde yaptığım değişikliğin tüm araçları etkilemesini sağladım. Ayrıca istenir ise, tek bir araç özelliği override edilerek, tüm araçlar için genel olan özellik değiştirilip, o araca özel hale getirilebilir.
Image Source: https://upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Decorator_UML_class_diagram.svg/800px-Decorator_UML_class_diagram.svg.png
Prototype:
Sıra geldi Creational Design Patternlerden Prototype’ı neden kullandığıma. Oyuna eklenen her arcın sürekli üretilmesi gerekmektedir. Oyun devam ettiği sürece, tanklar ve helikopterler random olarak yaratılmaktadır. Her seferinde oluşturulacak aracın tüm özellikleri atanarak, yeniden yaratılması yerine, daha en başta tekil tüm araçların yaratılıp bir container’a konup, daha sonra da istenene araç tipine göre yeni bir eşleniğin oluşturulması çok daha performanslı gözükmektedir. Aşağıda bu anlatım ile alakalı diyagram görülmektedir.
Image Source: https://www.gencayyildiz.com/blog/wp-content/uploads/2016/04/C-Prototype-Design-Pattern-Prototip-Tasarım-Deseni.png
Bir aracın tam bir eşleniğinin çıkarılması, yani DeepCopy işleminin TypeScript’de yapılabilmesi için, bu projede “lodash“kullanılmıştır. Aşağıda görüldüğü gibi lodash, ilgili komut ile projeye dahil edilir.
1 |
npm install --save lodash |
Clone Object : Bir nesnenin birebir kopyası lodash ile aşağıdaki gibi alınmaktadır.
1 |
let newWarMachine= _.cloneDeep(warMachine); |
Prototype Object: Araçların eşleniklerinin oluşturulması ve bir containerda saklanması için yazılan sınıf aşağıdaki gibidir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
export class VehicleManager { public VehicleObjects: Map<string, Vehicle>; constructor() { this.VehicleObjects = new Map<string, Vehicle>(); } public Add(item: Vehicle, name: string) { this.VehicleObjects.set(name, item); } public GetCloneVehicle(name: string) { if (this.VehicleObjects.has(name)) { return _.cloneDeep(this.VehicleObjects.get(name)); } } } |
Kullanım Şekli : Daha en başta oyunda kullanılabilecek tüm araçlar, decoration design pattern ile özellikleri birbirine aktarılıp oluşturulmuş ve “VehicleManager” container’ına eklenmişlerdir. Daha sonra istenen araç, prototype design patternine göre ilgili containerdan eşleniği oluşturulup çekilmektedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
import { Component, OnInit } from '@angular/core'; import _ from "lodash"; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { title = 'app'; ngOnInit() { var vm = new VehicleManager(); var warMachine = new WarMachine(25); var warMachineDecorator = new WarMachineDecorator(warMachine) var plane = new Plan(warMachineDecorator, 0, 15); var helicopter = new Helicopter(warMachineDecorator, 40, 30); vm.Add(warMachine, "WarMachine"); vm.Add(warMachineDecorator, "WarMachineDecorator"); vm.Add(plane, "Plane"); vm.Add(helicopter, "Helicopter"); let newWarMachine = vm.GetCloneVehicle("WarMachine"); newWarMachine.Fire(); newWarMachine.MoveForward(); let newWarMachineDecorator = vm.GetCloneVehicle("WarMachineDecorator"); newWarMachineDecorator.Fire(); newWarMachineDecorator.Escape(); newWarMachineDecorator.MoveForward(); let newPlane = vm.GetCloneVehicle("Plane"); newPlane.MoveDown(); newPlane.MoveUp(); newPlane.Fire(); newPlane.Escape(); newPlane.Stop(); let newHelicopter = vm.GetCloneVehicle("Helicopter"); newHelicopter.MoveForward(); newHelicopter.Fire(); newHelicopter.Escape(); } } |
Design Patternler kullanılması zorunlu olmayan ama kullanıldığı zaman hem kod kalabalığından kurtaran hem de genişlemeye çok açık yapıları bize sağlamaktadır. Bu projeden örnek vermek ister isek, araçlara ortak yeni özellik eklenmesi istenir ise, örneğin “Fuel” yani benzin özelliği sadece en basit araç tipine eklemek yeterlidir. Böylece istenen özellik bundan türüyen ve Decore edilen tüm araçlar için eklenmiş olacaktır. Ayrıc sürekli bir nesne üretimi olan senaryolarda, Prototype design patern’inin kullanılması ile her seferinde yeni nesnenin üretilmesi yerine var olan bir nesnenin deep copy’sinin oluşturulması performansda gözle görülür bir artışa sebep olmaktadır. Ben oyun yazarken bu 2 patternden bolca faydalandım. Umarım sizin de işinize yarar.
Geldik bir makalenin daha sonuna. Yeni bir makalede görüşmek üzere hepinize hoşçakalın.
Tüm Kod:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
import { Component, OnInit } from '@angular/core'; import _ from "lodash"; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { title = 'app'; ngOnInit() { var vm = new VehicleManager(); var warMachine = new WarMachine(25); var warMachineDecorator = new WarMachineDecorator(warMachine) var plane = new Plan(warMachineDecorator, 0, 15); var helicopter = new Helicopter(warMachineDecorator, 40, 30); vm.Add(warMachine, "WarMachine"); vm.Add(warMachineDecorator, "WarMachineDecorator"); vm.Add(plane, "Plane"); vm.Add(helicopter, "Helicopter"); let newWarMachine = vm.GetCloneVehicle("WarMachine"); newWarMachine.Fire(); newWarMachine.MoveForward(); let newWarMachineDecorator = vm.GetCloneVehicle("WarMachineDecorator"); newWarMachineDecorator.Fire(); newWarMachineDecorator.Escape(); newWarMachineDecorator.MoveForward(); let newPlane = vm.GetCloneVehicle("Plane"); newPlane.MoveDown(); newPlane.MoveUp(); newPlane.Fire(); newPlane.Escape(); newPlane.Stop(); let newHelicopter = vm.GetCloneVehicle("Helicopter"); newHelicopter.MoveForward(); newHelicopter.Fire(); newHelicopter.Escape(); } } export abstract class Vehicle { public abstract Fire(); public abstract MoveForward() } export class WarMachine extends Vehicle { protected _width: number; protected _height: number; protected _left: number; constructor(left: number) { super(); this._left = left; } public Fire() { console.log("Fire WarMachine"); } public MoveForward() { } } abstract class VehicleDecorator extends Vehicle { protected _vehicle: Vehicle; constructor(vehicle: Vehicle) { super(); this._vehicle = vehicle; } public Fire() { if (this._vehicle != null) this._vehicle.Fire(); } public MoveForward() { if (this._vehicle != null) { this._vehicle.MoveForward(); } } } export class WarMachineDecorator extends VehicleDecorator { constructor(vehicle: Vehicle) { super(vehicle); } public Fire() { if (this._vehicle != null) { this._vehicle.Fire(); } } public MoveForward() { if (this._vehicle != null) { this._vehicle.MoveForward(); } } public Escape() { for (var i = 0; i < 3; i++) { this.MoveForward(); } } public Stop() { } } export class Plan extends WarMachineDecorator { protected _speed: number; protected _top: number; protected _left: number; constructor(vehicle: Vehicle, top: number, speed: number) { super(vehicle); this._top = top; this._top = speed; } public Fire() { if (this._vehicle != null) { this._vehicle.Fire(); } } public MoveForward() { if (this._vehicle != null) { this._vehicle.MoveForward(); } } public MoveBack() { } public MoveUp() { } public MoveDown() { } } export class Helicopter extends WarMachineDecorator { protected _top: number; constructor(vehicle: Vehicle, top: number, speed: number) { super(vehicle); this._top = top; } public Fire() { if (this._vehicle != null) { this._vehicle.Fire(); } } public MoveForward() { if (this._vehicle != null) { this._vehicle.MoveForward(); } } } export class VehicleManager { public VehicleObjects: Map<string, Vehicle>; constructor() { this.VehicleObjects = new Map<string, Vehicle>(); } public Add(item: Vehicle, name: string) { this.VehicleObjects.set(name, item); } public GetCloneVehicle(name: string) { if (this.VehicleObjects.has(name)) { return _.cloneDeep(this.VehicleObjects.get(name)); } } } |
Source:
İyi günler. Hocam Microsoft un XNA adında oyun yapımını kolaylaştırmak için bir framework desteği varmış. Bununla ilgili bir yazı okudum ve çok hoşuma gitti acaba yakın zamanda C#/XNA/DirecX gibi araçları kullanarak bir oyunu 2D ve 3D tasarlama makalesi gelir mi? Oyunlarınızı daha efektif yazmak ve ilgi cekici, sürükleyici kılmak için bu araçların ne gibi artıları var, kullanabileceğimiz kütüphaneler ve Design pattern hakkında ayrıca detaylı bir makelenizide dört gözle bekliyor olacağız. İyi çalışmalar, esenlikle kalın.