首先,透過類別來分離出我們的遊戲的不同部分有關的理論。
遊戲中使用Class
一開始的遊戲結構上,沒有為了不同的遊戲部分裡的Events而使用Class。而是用了一個規則化的程式邏輯來寫這遊戲功能。在戰鬥循環動作中,讓Program類別只需打開Main()和去調用MainGame()。主要是初始者知道,開發遊戲裡涵蓋的基礎知識(移動世界各地,買東西..等。),這對於一般剛接觸用C#來開發遊戲的人來說,有許多不了解的地方。然後,我們需要一個類別來處理Battles,讓戰鬥流程不斷迴圈,就如同玩一款遊戲一樣,開始->玩遊戲->結速->再開始。現在需要打開一個全新的項目。把它叫做RpgTutorial。所以,進入“File - >New Project”。然後選擇“Console Application(控制台應用程式)”,並將其命名RpgTutorial。
現在,我們需要添加兩個類別,取名MainGame和Battle。右鍵單擊RpgTutorial中,選擇Add - >class,並將其命名為MainGame,並重複這個過程,叫它是Battle。現在,有三個基底類別的工作。讓Main()能呼叫MainGame類別。
namespace RpgTutorial
{
class Program
{
static void Main(string[] args)
{
MainGame maingame = new MainGame();
}
}
}
MainGame沒有建構函數的權利,但MainGame建構函數將不會發送給它任何參數。現在MainGame需要一個遊戲循環,並在該循環將在某個時候調用Battle class。所以要製造一個do/ while循環。
namespace RpgTutorial
{
class MainGame
{
Battle battle;
string answer;
public MainGame()
{
BasicGameLoop();
}
void BasicGameLoop()
{
do
{
battle = new Battle();
Console.WriteLine("Do you want to play
again?");
answer = Console.ReadLine();
}
while (answer == "Y" || answer == "y");
}
}
}
從上面的程式結構可以了解遊戲的「基本迴路」怎麼回事。此時的戰鬥不接受任何參數。這所以運行中畫面會很快的改變,因為沒有控制。此外,宣告我Battle class 到第二個步驟,而不是第一個步驟。
所以,現在有三個class來處理程序的不同狀態。一個用於加載,一個用於main game和一個用於battle class。這取決於遊戲的形式和目標。
規劃
現在,我們的基本結構是到位,但一定 還有需要加強的地方。首先,我們就要有一個英雄,一個怪物。我們最好為這兩個角色建構兩個新的類別,取名叫Hero和Monster。
Hero絕對是一個物件,但不會只有一種。如果我們調用一個新的Hero在程式的循環,每次開始將刪除英雄的統計和刷新數據,不是一個好方式。有什麼好的方法來設置這些數據呢?怎麼樣呼叫初始化裡面的Hero類別的靜態方法?其實,初始化變數不屬於英雄,但是當創建它時可以拉進來。用靜態的方式更好。呼叫初始化maingame類別中,需要發送Initialize()到創造了進行更新的英雄物件。要定義hero的變數為public。
namespace RpgTutorial
{
class Hero
{
public int CurrentHealth,
MaxHealth, CurrentMagic;
public int MaxMagic,
Strength, Defense, Agility;
public int Experience,
Gold, AttackDamage;
public string Identifier;
public bool isAlive;
public Hero(){}
public static void Initialize(Hero hero)
{
hero.CurrentHealth = 18;
hero.MaxHealth = 18;
hero.CurrentMagic = 8;
hero.MaxMagic = 8;
hero.Strength = 10;
hero.Defense = 3;
hero.Agility = 6;
hero.Experience = 0;
hero.Gold = 0;
Console.WriteLine("What is your Hero's
name?");
hero.Identifier =
Console.ReadLine();
hero.isAlive = true;
hero.AttackDamage = hero.Strength;
}
}
}
在這裡,Hero將設立當初的初始值,但是因為靜態方法不屬於另一個Hero,讓初始值仍保有一開始的值。
要記得如何設置的參數為Hero類別:Initialize(Hero hero)。這不是要求一個新的物件,或者一個新的宣告,這就像一個empty,顯示它可以被傳遞給它的物件期望的方法。如果嘗試將不同類別的物件不止一個來自像myhero英雄類別,將會產生錯誤。
所以現在MainGame需要更新,呼叫Hero類別,並初始化Hero的變數。
namespace RpgTutorial
{
class MainGame
{
Hero myhero;
Battle battle;
string answer;
public MainGame()
{
myhero = new Hero();//創立Hero 類別
Hero.Initialize(myhero);//傳送 myhero到Initialize的Hero
//可以變動的,不論初始化的是不是呼叫Hero
BasicGameLoop();
}
void BasicGameLoop()
{
do
{
battle = new Battle();
Console.WriteLine("Do you want to play
again?");
answer = Console.ReadLine();
}
while (answer == "Y" || answer == "y");
}
}
}
在註解裡,表非可以在不同的地方使用不同的名稱,同時仍然使用相同的物件。要注意的是一定要宣告Hero,之前的遊戲循環,沒有創建它的一個新的副本。現在myhero將從這裡開始了使用英雄副本,換句話說,這是將調用一個新的Hero。
Monster 類別
Monster類別需要有相同類型的變數作為Hero。不需要一個initialize方法,因為有Monster的多個副本。所以保持Monster類別簡單多了。
namespace RpgTutorial
{
class Monster
{
public int CurrentHealth,
MaxHealth, CurrentMagic;
public int MaxMagic,
Strength, Defense, Agility;
public int Experience,
Gold, AttackDamage;
public string Identifier;
public bool isAlive;
public Monster()
{
CurrentHealth = 8;
MaxHealth = 8;
CurrentMagic = 0;
MaxMagic = 0;
Strength = 5;
Defense = 3;
Agility = 4;
Experience = 5;
Gold = 2;
Identifier = "Monster";
isAlive = true;
AttackDamage = Strength;
}
}
}
正如你可以看到剛剛初始化權的構造函數中的變量。這樣一來,當要求它的一個新實例,它將被設置並準備好了。又一切都公開,因為我們需要的一切將我們的戰鬥類的內部使用。
寫死的程式碼
「寫死的程式碼」是一件很遭的情況。寫死的程式碼是把輸出或輸入的相關參數(例如:路徑、輸出的形式、格式) 直接寫死在原始碼中,而不是再次重複使用一個變數的值到程式中。在英雄職業,怪物的class,是寫死的程式碼在所謂認定的結果論裡。這不能真正避免了這一些流程,但要在程序中使用這些變數,而不是直接輸入一個數字英寸,寫死的程式碼不僅使你的程式不夠靈活,而且更難以改變。比方說,我們有一個球是像素25寬,像素 25高。而不是告訴該程式直接寫死值的大小25x25,反而會告訴它要檢查圖片的寬度和高度。所以這就是Design Pattern的用意,雖然這會程式結構分工的更細,但如果不這樣做,當決定改變球的尺寸30X30 時,必須通過整個程式的修改,並更改25X25到30X30。如果將程式的意圖改成告訴它來檢查圖片的寬度和高度,其實只要變數改變,並不會改變程式整個架構。
在一個大型的專案,如果開發一個巨大的商業遊戲,它決定了開始的基本值,之後想改變是非常困難且更費工和開發成本,如果已經發行的遊戲,這不是一個快速和容易解決的程式結構。在設計上,可以像是將參數寫在一個XML裡。寫死的程式碼是會造成日後開發的時間和成本,所以要避免這種寫法。
戰鬥類別
在這一點上,這應該是相當熟悉的。有一個Loop,檢查每一個正確的絛件,並顯示造成多大的損害。得到了特性上的資訊,可以用它來確定類別的東西造成多大的損害或者後續的動作,所以要作出一個建構函數來接受一個Hero和Monster戰鬥類別:
namespace RpgTutorial
{
class Battle
{
public Battle(Hero hero, Monster monster){}
}
}
現在有完整的存取狀態。另外,本來可以為角色設置health、magic等參數,但是這是很簡單的。現在來開始循環。首先介紹,然後我們循環,將放進新的方法Method,它會透過bool檢查是否存活,而不是health。
namespace RpgTutorial
{
class Battle
{
public Battle(Hero hero, Monster monster)
{
Console.WriteLine("{0} is facing a {1}.", hero.Identifier,
monster.Identifier);
}
public void
BattleLoop(Hero hero, Monster monster)
{
do
{
//實現
}
while (hero.isAlive == true && monster.isAlive == true);
}
}
}
傳送物件到新的方法,如此一來就能存取這些狀態,現在需要一個方法去顯示狀態:
public void
PrintStatus(Hero hero, Monster monster)
{
Console.Write(@"
********************************
HP/MaxHP MP/MaxMP
{0}: {1}/{2}hp {3}/{4}mp
{5}: {6}/{7}hp {8}/{9}mp
********************************
",
hero.Identifier,
hero.CurrentHealth,
hero.MaxHealth,
hero.CurrentMagic,
hero.MaxMagic,
monster.Identifier,
monster.CurrentHealth,
monster.MaxHealth,
monster.CurrentMagic,
monster.MaxMagic
);
}
再一次傳遞物件到新的方法,在請求時將會印出兩個角色狀態的方法。這是可以運行來仔細檢查並除錯,看看它是否運作正確。首先,需要確保在創建Main game class的new monster,然後需要更新battle類別同時接受myhero和monster。下面是更新MainGame和BattleClass:
namespace RpgTutorial
{
class MainGame
{
Hero myhero;
Battle battle;
string answer;
public MainGame()
{
myhero = new Hero();
Hero.Initialize(myhero);
BasicGameLoop();
}
void BasicGameLoop()
{
do
{
Monster monster = new Monster();
battle = new Battle(myhero, monster);
Console.WriteLine("Do you want to play
again?");
answer = Console.ReadLine();
}
while (answer == "Y" || answer == "y");
}
}
}
namespace RpgTutorial
{
class Battle
{
public Battle(Hero hero, Monster monster)
{
Console.WriteLine("{0} is facing a {1}.", hero.Identifier,
monster.Identifier);
BattleLoop(hero,
monster);
}
public void
BattleLoop(Hero hero, Monster monster)
{
do
{
PrintStatus(hero, monster);
Console.ReadLine();
}
while (hero.isAlive == true && monster.isAlive == true);
}
public void
PrintStatus(Hero hero, Monster monster)
{
Console.Write(@"
********************************
HP/MaxHP MP/MaxMP
{0}: {1}/{2}hp {3}/{4}mp
{5}: {6}/{7}hp {8}/{9}mp
********************************
",
hero.Identifier,
hero.CurrentHealth,
hero.MaxHealth,
hero.CurrentMagic,
hero.MaxMagic,
monster.Identifier,
monster.CurrentHealth,
monster.MaxHealth,
monster.CurrentMagic,
monster.MaxMagic
);
}
}
}
到目前為止,呈現出一個menu,但真正從這裡算是簡單且順暢。所有要做的是一個printchoice然後再處理的攻擊,這是很基本的遊戲程式結構。但是還是要解決AI。一些更多的技巧和調整,一個相當實用的Battle loop。遊戲的開發,還有更多的元素,是要透過遊戲呈現,聲音,影像,動畫效果,攻擊,防禦,移動。在此介紹基本的遊戲設計觀。
-雲遊山水為知已逍遙一生而忘齡- 電腦神手
沒有留言:
張貼留言