2016-03-20 60 views
5

我想編程一個遊戲,並且想爲多個實體使用組件模式。Nim - 創建實現方法的對象序列

在接口/類型/多繼承的語言中,沒有問題。

我想要一些實體可更新但不可呈現,有些應該是兩者。


哈斯克爾:

class Updateable a where 
    update :: Float -> a -> a 

class Renderable a where 
    render :: a -> Picture 

class InputHandler a where 
    handleInput :: Event -> a -> a 

我可以創造的東西,可以更新列表。

updateAll :: Updateable a => Float -> [a] -> [a] 
updateAll delta objs = map (update delta) objs 

在Java/d/...這可以通過接口

interface Updateable { 
    void update(float delta); 
} 

// somewhere in a method 
List<Updateable> objs = ...; 
for (Updateable o : objs) { 
    o.update(delta); 
} 

現在我想知道如何能在NIM實現與多方法來實現。

擬合multimethod的存在是否可以用類型表示?

var objs: seq[???] = @[] 



編輯:添加更多的代碼和固定不正確的Haskell例如

+0

好像[概念](http://nim-lang.org/docs/manual.html#generics-concepts)是的路要走,但他們仍然在製品和我不能創建一個'序列[可更新] '。 '類型可更新=概念x更新(x)' – Karroffel

+0

在你的例子中,你不清楚你要如何使用多繼承。例如,爲什麼不簡單地不做任何事情,如果一個組件不「實現」render()或update()?查看受歡迎的引擎的基於組件的體系結構(Unity3d,UE4),他們不會使用用戶實現的核心類的接口 - 考慮單個「組件」類型比可能需要實現的許多接口更容易。 – endragor

回答

3

我不確定這是否回答您的問題,但值得一提。

如果您要將遊戲對象存儲在基於類型的單獨列表中,則仍然可以編寫大量通用邏輯。由於預讀和分支預測,按類型存儲對象具有更好的性能。看到這個講座,從一個應該知道他在說什麼的人:Multiprocessor Game Loops: Lessons from Uncharted 2: Among Thieves。例如,如果您已經爲某些對象類型定義了texture proc,那麼您可以編寫適用於所有對象的通用draw(t: T) = magicRenderToScreen(texture(t)) proc。如果您正在實施資源池或實際任何一種常規行爲,這也很有用。

你必須在渲染和更新循環中包含每個受影響的對象類型,但這在實踐中通常不是什麼大問題。你甚至可以用一個簡單的宏,使這個更簡潔,讓你渲染循環只包含類似renderAll(players, enemies, sprites, tiles)

泛型列表不是編譯語言直白,和NIM迫使你看到它,這是一種很好的辦法正在製作一款遊戲。要擁有泛型列表,您通常必須使用指針和動態分派,或某種聯合類型。我似乎記得,nim曾經能夠從父對象引用(它可以使列表包含幾種類型並在運行時動態分配)分派到正確的多方法,但是我真的不確定它是否仍然可以做...?

更有知識的人請告訴我們!

+0

感謝您的回答。基於公共超類的動態分派仍然可以完成,但我嘗試通過接口(不存在)模擬多重繼承。我將與多個不同類型的列表一起去。 – Karroffel

+0

更多地思考它,我猜也可能有行爲列表,然後將這些列表的引用組成遊戲對象。 – Jostein

3

缺乏明確interface關鍵字是common question in the Nim community。以Araq答案,並把它應用到基於您的Java/d片斷一個假設的情況下,我們可以寫這樣的事:

import strutils # For formatFloat 

type 
    IUpdateable = 
    tuple[ 
     update: proc(v: float) {.closure.}, 
     show: proc(): string {.closure.} 
     ] 

    Rounded = ref object 
    internalValue: float 

    Real = ref object 
    a_real_value: float 

# Here goes our rounded type. 
proc `$`(x: Rounded): string = 
    result = "Rounded{" & $int(x.internalValue) & "}" 

proc updateRounded(x: Rounded, delta: float) = 
    x.internalValue += delta 

proc getUpdateable(x: Rounded): IUpdateable = 
    result = (
    update: proc(v: float) = x.updateRounded(v), 
    show: proc(): string = `$`(x) 
    ) 

converter toIUpdateable(x: Rounded): IUpdateable = 
    result = x.getUpdateable 

# Here goes our Real type. 
proc `$`(x: Real): string = 
    result = "Real{" & 
    x.a_real_value.format_float(precision = 3) & "}" 

proc update_real(x: Real, delta: float) = 
    x.a_real_value += delta 

proc getUpdateable(x: Real): IUpdateable = 
    result = (
    update: proc(v: float) = x.update_real(v), 
    show: proc(): string = `$`(x) 
    ) 

# Here goes the usage 
proc main() = 
    var objs: seq[IUpdateable] = @[] 
    var a = Rounded() 
    var b = Real() 
    a.internalValue = 3.5 
    b.a_real_value = 3.5 

    objs.add(a) # works because of toIUpdateable() 
    objs.add(b.getUpdateable) 

    for obj in objs: 
    echo "Going through one loop iteration" 
    echo "\t", obj.show() 
    obj.update(0.4) 
    echo "\t", obj.show() 
    obj.update(0.4) 
    echo "\t", obj.show() 

main() 
# -> Going through one loop iteration 
# -> Rounded{3} 
# -> Rounded{3} 
# -> Rounded{4} 
# -> Going through one loop iteration 
# -> Real{3.50} 
# -> Real{3.90} 
# -> Real{4.30} 

但是,你可以read in that forum thread,這取決於究竟你需要的接口,其他方法可能更好。另外,大概未來的路線是concepts,但像往常一樣,手冊是乾的,the related unit tests are cryptic,所以我無法將之前的元組示例翻譯成概念。

如果您覺得要理解概念,您應該直接在論壇上提問,但請注意,如手冊所述,concepts are still in development

+0

這似乎是概念方法是死路一條。概念沒有運行時表示。永遠不可能創建一個'seq [Updateable]'。使用閉包的元組感覺不對。這就像構建你自己的vtable,因爲編譯器不能。我希望Nim在1.0版本之前獲得接口 – Karroffel

+1

一般來說,使用Nim的想法是它[讓您有可能實現這些功能](https://bitbucket.org/fowlsoft/interfaces/wiki/Home),就像那裏沒有官方的OOP宏,[但是你可以自己創建](http://nim-by-example.github.io/oop_macro/)。 –

相關問題