Sh.Tac.
>например сериализация у меня сейчас выглядит очень просто, если кто-то унаследован от такого пустого IBase, я вызываю его метод, если нет, - то обычный memcpy () для POD
memcpy это не сериализация, даже для POD.
Часто базовый класс выглядит на подобии вот этого:
class GameObject { void update(float dt); void render( RenderTarget target); }
Иногда нужно изменить поведение некотрому экземпляру, не трогая другие.
Думаю сделать базовый класс вот таким образом, на колбеках:
class GameObject { GameObject() { update = &defaultUpdate; render = &defaultRender; } void ( *update)( float); void ( *render)( void); void defaultUpdate( float dt){...} void defaultRender( ){...} }
И если нужно сделать один экземпляр особенным, то можно просто присвоить лямбду. Хорошая ли это идея?
AvrDragon
> Хорошая ли это идея?
переизобретаем наследование?
AvrDragon
> Иногда нужно изменить поведение некотрому экземпляру, не трогая другие.
может этот экземпляр просто другого класса?
berserkovich
конечно можно унаследовать класс и перегрузить нужный метод, но плодить классы для каждой мелочи загрязняет пространство имен
Pushkoff
У меня есть 10 самолетиков. Летят они цепочкой. Хочу чтоб 7ой выстрелил.
Конечно можно сделать подкласс, но мое* решение покрасивей и покомпактней вроде как. Или нет?
*сомневаюсь что я первый додумался
AvrDragon
> плодить классы для каждой мелочи загрязняет пространство имен
чистота пространства имён - последняя вещь, о которой бы я беспокоился
такой подход может быть оправдан при реализации состояний как замена else if/switch. это при условии, что один и тот же экземляр может поменять этот стейт в течении жизни
и скорее всего это будет где-то внутри update, а не полная его замена. а для render это вообще очень сомнительно
AvrDragon
> У меня есть 10 самолетиков. Летят они цепочкой. Хочу чтоб 7ой выстрелил.
и эта задача у тебя требует подмены update и draw? мне тебя жаль...
надели все самолетики возможностью выстрелить, а стреляет пусть 7, либо посылай ему сообщение либо пусть сами думают кто должен выстрелить
либо сделай 9 самолетиков не стреляющими, а седьмой стреляющим
berserkovich
>чистота пространства имён - последняя вещь, о которой бы я беспокоился
это пока их мало
>такой подход может быть оправдан при реализации состояний как замена else if/switch. это при условии, что один и тот же экземляр может поменять этот стейт в течении жизни
это совсем другая тема
лично мне такая возможность динамически наделить обьект особыми свойствами не прибегая к наследованию кажется довольно выразительной и чистой в сравнении с другими возмозжностями
>а для render это вообще очень сомнительно
Для render и правда маловероятно, хотя ситуацию тоже множно себе представить.
Pushkoff
>и эта задача у тебя требует подмены update и draw?
А почему б и не подменить update()? как будто подмена метода лямбдой очень долгое дело.
>мне тебя жаль...
:3
>надели все самолетики возможностью выстрелить, а стреляет пусть 7
грязно
>либо посылай ему сообщение
>либо пусть сами думают кто должен выстрелить
тоже предпологает что стрелять умеют все, а не только тот который надо
>либо сделай 9 самолетиков не стреляющими, а седьмой стреляющим
вот я же так и пытаюсь сделать
AvrDragon
ты зря выбрал С++, посмотри на питон или flash. там твои методы более применимы
AvrDragon
> вот я же так и пытаюсь сделать
так это 2 класса, стреляющий самолет и не стреляющий
а твои функторы лучше вынести в скрипт, то что ты делаешь это адъ и ужосъ
nes
> Думаю у многих тут (которые пишут свои игры) есть нечто подобное, предлагаю
> показать и обсудить свои творения )
Ну смотри:
#ifndef BREAKOUT_GAME_ACTOR_H #define BREAKOUT_GAME_ACTOR_H #pragma once #include "event_system.h" #include "processes.h" #include "serializer.h" #include "actor_data_component.h" namespace pegas { namespace dream3d { using namespace pegas::core; // class GameLogic; class GameActor; struct CollisionData; typedef SmartPointer<GameActor> GameActorPtr; typedef GameLogic* GameLogicPtr; typedef size_t ACTORID; typedef String ACTORTYPE; class GameActor { public: enum eDataScope { k_actorDataAll = 1, k_actorDataCommon, k_actorDataInstance, }; typedef size_t DataScope; static const String k_unamedActor; static const ACTORID k_actorIdNull; public: GameActor(); virtual ~GameActor( ) {} //general events virtual void onCreate( ) {} virtual void onDestroy( ) {} virtual void onCollisionEnter( ACTORID sender, const CollisionData& eventData) {} virtual void onCollisionLeave( ACTORID sender, const CollisionData& eventData) {} virtual void onTick( MILLISECONDS deltaTime) {} ProcessPtr getProcess( ); virtual bool isTickable( ) const { return false; }; //возвращает тип обьекта ("персонаж", "монстр", "ящик" и т.д.) //обычно используеться при приобразовании указателя на базовый класс в указатель //на конкретный класс игрового обьекта virtual ACTORTYPE getType( ) const = 0; void setID( ACTORID actorID) { m_id = actorID; } ACTORID getID( ) const { return m_id; } void setName( const String& name) { m_name = name; } String getName( ) const { return m_name; } void setParentID( ACTORID actorID) { m_parentID = actorID; } ACTORID getParentID( ) const { return m_parentID; } void setGroupName( const String& name) { m_groupName = name; } String getGroupName( ) const { return m_groupName; } void addComponent( ActorDataPtr component); void removeComponent( const ComponentID& name); void removeComponentsByType( const ComponentType& _type); bool hasComponent( const ComponentID& name); bool hasComponentOfType( const ComponentType& name); ActorDataPtr getComponent( const ComponentID& name); void getComponentsByType( const ComponentType& name, std::list<ActorDataPtr>& _out); bool renameComponent( ActorDataPtr component, const ComponentID& name); bool enableComponent( const ComponentID& name); void disableComponent( const ComponentID& name); virtual GameActorPtr clone( ) const; virtual void parse( const pugi::xml_node& data, DataScope scope); virtual void load( ISerializer& serializer, DataScope scope); virtual void save( ISerializer& serializer, DataScope scope); protected: ACTORID m_id; //уникальный ИД, назначаемый менеджером игровой логики String m_name; //имя (обычно устанавливаемое пользователем в редакторе, //которое используеться в скриптах ACTORID m_parentID; String m_groupName; //этот метод непосредственно создает экземпляр обьекта //и должен переопределяться в подклассах virtual GameActorPtr _clone( ) const = 0; ProcessPtr m_process; /* friend class GameLogic; GameLogicPtr m_gameLogic; //класс игровой логики устанавливает ссылку на себя напрямую //=> делаем его дружественным*/ typedef std::multimap<ComponentType, ActorDataPtr> ComponentsTypeMap; typedef ComponentsTypeMap::iterator ComponentsTypeMapIt; typedef std::map<ComponentID, ActorDataPtr> ComponentsIDMap; typedef ComponentsIDMap::iterator ComponentsIDMapIt; ComponentsIDMap m_components; ComponentsTypeMap m_typeComponents; void notifyUpdate( const ComponentType& component, const String& propertyName = _text( "all")); private: ComponentID generateComponentID( const ComponentType& _type); GameActor( const GameActor& src); GameActor& operator=( const GameActor& src); }; class ProcessProxy: public Process { public: ProcessProxy( GameActor* actor) :m_actor( actor) { assert( actor != 0 && "invalid argument"); } virtual void update( MILLISECONDS deltaTime) { m_actor->onTick( deltaTime); } private: GameActor* m_actor; }; } } #endif [/cpp]
Pushkoff
>ты зря выбрал С++, посмотри на питон или флаш. там твои методы более применимы
я собственно на шарпе сейчас пишу, с++ просто для примера, там это будет через делегаты конечно
>так это 2 класса, стреляющий самолет и не стреляющий
которые отличаются только одним методом, самое оно для лямбд
>а твои функторы лучше вынести в скрипт, то что ты делаешь это адъ и ужосъ
А чем они плохи??? Они абсолютно опциональны - не хочешь, используй стандартные функции-члены, перегружай если надо. Но вот если не хочеться плодить лишних классов из-за каждой мелочи, то метод с заменой функции-члена намного более элегантен.
>так это 2 класса,
Тем более наследование очень скоро станет очень непонятным, если один стреляет, другой облетает сбоку, третий помигивает, четвертый делает все одновременно, кроме помигивания и что-то еще. А на колбеках все очень тонко настраивается, при этом никакого риска - не надо не используй.
Единственный минус который я вижу это несколько усложняет понимание интерфейса класса - методов update() и render() как таковых нет, вместо них defaultUpdate() и defaultRender() и именно их и нужно перегружать.
А так не хочешь не используй, но одновременно такая гибкая опция доступна.
AvrDragon
> которые отличаются только одним методом, самое оно для лямбд
не вопрос. это и есть разделение представление и логики и это хорошо, и хорошо что ты к этому пришел. но тогда лучше логику выделить в один класс со своим интерфейсом и спрашивать у этого класса что делать.
Албанец
а что, без map* никак нельзя ? :)
Албанец
у тебя в одном объекте собрано слишком много функционала, попробуй разобрать на части и собрать миксином
innuendo
> а что, без map* никак нельзя ? :)
можно, но мне удобней ТАК.
Pushkoff
> у тебя в одном объекте собрано слишком много функционала, попробуй разобрать на
> части и собрать миксином
Чушь, ничего лишнего там нет. Ровно столько, сколько нужно. А для сравнения, где "много функционала", гляньте базовый класс Actor.uc из UDK, написанный эпиками, где закодировано все вселенское бытие.
Тема в архиве.