我正在制作一個簡單的回合制農業模擬器游戲,玩家可以選擇是購買土地還是農作物,并根據回合數進行種植。不同的作物生長時間不同,收購價格和銷售價格也不同。目標是成為第一個達到美元金額的人。
我的問題是如何以編程方式開發這些作物。我目前將每種作物變異作為作物的子類,但是,這會導致大量冗余(主要是不同的欄位/屬性值或影像路徑)。由于這些物件除了某些值之外非常相似,它們應該是子類,還是使用 Enum 型別制作一個類 Crop 并使用邏輯來確定它應該具有的值是更好的做法?
超類作物
- 小麥亞綱
- 玉米亞種
- 大麥亞綱
或者
Crop.Type = CropType.Wheat
if(this.Type == CropType.Wheat) { return StockMarket.Wheat_Sell_Value; }
else if(this.Type == CropType.Corn) { return StockMarket.Corn_Sell_Value; }
uj5u.com熱心網友回復:
如果你創建一個單一的裁剪類,它有變得非常大和笨拙的風險,特別是如果你想添加一個新的裁剪型別,你必須更新代碼中散落的 100 條 if 陳述句(例如if(this.Type == CropType.Wheat) { return StockMarket.Wheat_Sell_Value; })。
要回應@oswin 的回答,請謹慎使用繼承。您可能可以使用具有一些“愚蠢”屬性的基類,但在添加任何實作“行為”或復雜性的內容時要特別小心,例如方法和邏輯;即任何作用于CropType內部的東西Crop都可能是一個壞主意。
一種簡單的方法是,如果作物型別都具有相同的屬性,但只是不同的值;所以作物實體只是由游戲中的行程來處理,見下文。 (注意:如果作物具有不同的屬性,那么我可能會使用介面來處理它,因為它們在您需要進行更改時更加寬容)。
// Crop Types - could he held in a database or config file, so easy to add new types.
// Water, light, heat are required to grow and influence how crops grow.
// Energy - how much energy you get from eating the crop.
Name:Barley, growthRate:1.3, water:1.3, light:1.9, heat:1.3, energy:1.4
Name:Corn, growthRate:1.2, water:1.2, light:1.6, heat:1.2, energy:1.5
Name:Rice, growthRate:1.9, water:1.5, light:1.0, heat:1.4, energy:1.8
裁剪型別值有助于稍后驅動邏輯。您還(我假設)需要作物實體:
class CropInstance
{
public CropType Crop { get; set; }
public double Size { get; set; }
public double Health { get; }
}
然后,您只需讓程式的其他部分作用于 Crop 實體,例如:
void ApplyWeatherForTurn(CropInstance crop, Weather weather)
{
// Logic that applies weather to a crop for the turn.
// E.g. might under or over supply the amount of water, light, heat
// depending on the type of crop, resulting in 'x' value, which might
// increase of decrease the health of the crop instance.
double x = crop.WaterRequired - weather.RainFall;
// ...
crop.Health = x;
}
double CurrentValue(CropInstance crop)
{
return crop.Size * crop.Health * crop.Crop.Energy;
}
請注意,您仍然可以添加對不同作物執行不同操作的邏輯,但基于它們的值,而不是它們的型別:
double CropThieves(CropInstance crop)
{
if(crop.health > 2.0 & crop.Crop.Energy > 2.0)
{
// Thieves steal % of crop.
crop.Size = crop.Size * 0.9;
}
}
更新 - 介面:
我又想了一會兒。類似代碼的假設double CurrentValue(CropInstance crop)是它假設您只處理作物實體。如果您要添加其他型別(如 Livestock),則此類代碼可能會變得很麻煩。
例如,如果你確定你永遠只有莊稼,那么這個方法就很好。如果您決定稍后添加另一種型別,它將是可管理的,如果您變得非常流行并決定添加 20 種新型別,您將需要重新撰寫/重新架構,因為它不會從維護中很好地擴展看法。
This is where interfaces come in, imagine you will eventually have many different types including Crop (as above) and Livestock - note it's properties aren't the same:
// growthRate - how effectively an animal grows.
// bredRate - how effectively the animals bred.
Name:Sheep, growthRate:2.1, water:1.9, food:2.0, energy:4.6, bredRate:1.7
Name:Cows, growthRate:1.4, water:3.2, food:5.1, energy:8.1, breedRate:1.1
class HerdInstance
{
public HerdType Herd { get; set; }
public int Population { get; set; }
public double Health { get; }
}
So how would interfaces come to the rescue? Crop and herd specific logic is located in the relevant instance code:
// Allows items to be valued
interface IFinancialValue
{
double CurrentValue();
}
class CropInstance : IFinancialValue
{
...
public double CurrentValue()
{
return this.Size * this.Health * this.Crop.Energy;
}
}
class HerdInstance : IFinancialValue
{
...
public double CurrentValue()
{
return this.Population * this.Health * this.Herd.Energy - this.Herd.Food;
}
}
You can then do things with objects that implement IFinancialValue:
public string NetWorth()
{
List<IFinancialValue> list = new List<IFinancialValue>();
list.AddRange(Crops);
list.AddRange(Herds);
double total = 0.0;
for(int i = 0; i < list.Count; i )
{
total = total list[i].CurrentValue();
}
return string.Format("Your total net worth is ${0} from {1} sellable assets", total, list.Count);
}
You might recall that above I said:
...but be especially careful when adding anything that implements "behaviour" or complexity, like methods and logic; i.e. anything that acts on CropType within Crop is probably a bad idea.
...這似乎與上面的代碼相矛盾。不同之處在于,如果您有一個包含所有內容的類,您將無法靈活使用,在上面的方法中,我假設我可以通過使用 [ x]型別和[x]實體架構。
uj5u.com熱心網友回復:
答案取決于作物型別之間的功能差異。一般規則是在可能的情況下避免不必要的復雜性,并且應該謹慎使用繼承,因為它引入了硬依賴關系。
因此,如果所有作物在功能上都相似并且僅因屬性值不同而不同,那么您可能希望為作物使用單個類,但如果您的游戲邏輯要求作物型別表現得非常不同和/或攜帶非常不同的資料集,那么您可能需要考慮創建單獨的結構。
如果不知道您的游戲世界的確切細節,也無法回答繼承是否是最佳選擇(如果您需要單獨的結構)。您可以考慮的一些替代方案是:
- 介面(或其他型別的混合),它允許您跨多種型別重復使用行為或資料,例如,如果可以收獲作物,也許也可以收獲森林。
- 結構(或資料類),它只定義資料結構而不定義行為。這通常更有效,并迫使您使用更少的抽象進行更簡單的設計。
- 一種函式式編程方法,其中作物僅作為傳遞給函式的原語存在。這具有函式式編程的所有優點,例如沒有副作用、錯誤更少、更容易撰寫測驗、更簡單的并發設計可以幫助您的游戲規模更大。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/314606.html
