2016-03-20 2 views
5

Я хочу запрограммировать игру и хотел бы использовать шаблон компонента для нескольких объектов.Nim - создать последовательность объектов, которые реализуют метод

На языке с интерфейсами/типами/множественным наследованием проблем не было.

Я хочу, чтобы некоторые объекты были обновляемыми, но не отображаемыми, а некоторые должны быть обоими.


Haskell:

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 с мультиметодов.

Может ли существование подходящего мультиметода быть выраженным в типе?

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



Edit: Добавлен дополнительный код и фиксированный неправильный пример Haskell

+0

Похоже, [концепции] (http://nim-lang.org/docs/manual.html#generics-concepts) - это путь, но они все еще являются WIP, и я не могу создать 'seq [Updatable] '. 'type Updateable = concept x update (x)' – Karroffel

+0

Непонятно, как именно вы хотели бы использовать множественное наследование в своем примере. Например, почему бы просто не делать ничего, если компонент не «реализует» render() или update()? Рассматривая архитектуру на основе компонентов популярных процессоров (Unity3d, UE4), они не используют интерфейсы для основных классов, которые реализуют пользователи, - проще думать об одном типе «Компонент», чем о многих интерфейсах, которые могут потребоваться реализовать. – endragor

ответ

3

Я не уверен, отвечает ли это на ваш вопрос, но стоит упомянуть.

Если вы хотите хранить игровые объекты в отдельных списках на основе типа, вы все равно можете написать много общей логики. Хранение объектов по типу имеет лучшую лучшую производительность из-за предсказания чтения и ветвления. См. Эту лекцию от парня, который должен знать, о чем он говорит: Multiprocessor Game Loops: Lessons from Uncharted 2: Among Thieves.

Например, если вы определили какой-либо из ваших типов объектов texture, то вы можете написать генератор draw(t: T) = magicRenderToScreen(texture(t)), который будет работать для всех из них. Это также полезно, если вы реализуете пулы ресурсов или какое-либо общее поведение.

Вам нужно включить какой-либо тип затронутого объекта в циклы рендеринга и обновления как-то, но на практике это обычно не очень важно.Вы можете даже использовать простой макрос, чтобы сделать это менее подробным, поэтому ваш цикл рендеринга просто содержит что-то вроде renderAll(players, enemies, sprites, tiles)

Общие списки не являются простыми на скомпилированных языках, и nim заставляет вас видеть это, что является хорошим, когда вы «Работа над игрой. Чтобы иметь общие списки, вы обычно должны использовать указатели и динамическую диспетчеризацию или какой-либо тип объединения. Кажется, я помню, что nim имел обыкновение отправлять правильные мульти-методы из родительского объекта ref (что позволило бы спискам содержать несколько типов и динамически отправлять во время выполнения), но я честно не уверен, может ли это быть сделанный...?

Кто-то более осведомленный, пожалуйста, дайте нам знать!

+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), как и там не является официальным макросом ООП [но вы можете создать свой собственный] (http://nim-by-example.github.io/oop_macro/). –

Смежные вопросы